diff options
70 files changed, 1043 insertions, 575 deletions
diff --git a/.gitignore b/.gitignore index 2a7e605a350..1210ac3b44a 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ db/data.yml vendor/bundle/* rails_best_practices_output.html doc/code/* +.secret diff --git a/CHANGELOG b/CHANGELOG index f21a9e8a943..8ef925702c6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,14 @@ +v 5.3.0 + - Refactored services + - Campfire service added + - HipChat service added + - Fixed bug with LDAP + git over http + - Fixed bug with google analytics code being ignored + - Improve sign-in page if ldap enabled + - Respect newlines in wall messages + - Generate the Rails secret token on first run + - Rename repo feature + v 5.2.0 - Turbolinks - Git over http with ldap credentials diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d13909b8a0..7038f76ff88 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Issues and pull requests not in line with the guidelines listed in this document ## Issue tracker -The [issue tracker](https://github.com/gitlabhq/gitlabhq/issues) is only for obvious bugs or misbehavior in the master branch of GitLab. When submitting an issue please conform to the issue submission guidelines listed below. +The [issue tracker](https://github.com/gitlabhq/gitlabhq/issues) is only for obvious bugs or misbehavior in the latest [stable or development release of GitLab](MAINTENANCE.md). When submitting an issue please conform to the issue submission guidelines listed below. Do not use the issue tracker for feature requests. We have a specific [feedback and suggestions forum](http://feedback.gitlab.com) for this purpose. @@ -101,6 +101,12 @@ gem "foreman" # Cache gem "redis-rails" +# Campfire integration +gem 'tinder', '~> 1.9.2' + +# HipChat integration +gem "hipchat", "~> 0.9.0" + group :assets do gem "sass-rails" gem "coffee-rails" @@ -116,7 +122,7 @@ group :assets do gem "jquery-ui-rails", "2.0.2" gem "modernizr", "2.6.2" gem "raphael-rails", git: "https://github.com/gitlabhq/raphael-rails.git" - gem 'bootstrap-sass', "2.2.1.1" + gem 'bootstrap-sass' gem "font-awesome-sass-rails", "~> 3.0.0" gem "gemoji", "~> 1.2.1", require: 'emoji/railtie' gem "gon" @@ -168,14 +174,14 @@ group :development, :test do gem 'rb-inotify', require: linux_only('rb-inotify') # PhantomJS driver for Capybara - gem 'poltergeist', git: 'https://github.com/jonleighton/poltergeist.git', ref: '9645b52009e258921b860d3b7601d00008b22c45' + gem 'poltergeist', '~> 1.3.0' gem 'spork', '~> 1.0rc' end group :test do gem "simplecov", require: false - gem "shoulda-matchers", "1.3.0" + gem "shoulda-matchers", "~> 2.1.0" gem 'email_spec' gem "webmock" gem 'test_after_commit' diff --git a/Gemfile.lock b/Gemfile.lock index 93798439d8c..48be954c0c5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,16 +12,6 @@ GIT specs: raphael-rails (2.1.0) -GIT - remote: https://github.com/jonleighton/poltergeist.git - revision: 9645b52009e258921b860d3b7601d00008b22c45 - ref: 9645b52009e258921b860d3b7601d00008b22c45 - specs: - poltergeist (1.1.0) - capybara (~> 2.0, >= 2.0.1) - faye-websocket (~> 0.4, >= 0.4.4) - http_parser.rb (~> 0.5.3) - GEM remote: https://rubygems.org/ specs: @@ -64,7 +54,7 @@ GEM erubis (>= 2.6.6) binding_of_caller (0.7.1) debug_inspector (>= 0.0.1) - bootstrap-sass (2.2.1.1) + bootstrap-sass (2.3.1.0) sass (~> 3.2) builder (3.0.4) capybara (2.1.0) @@ -76,7 +66,7 @@ GEM carrierwave (0.8.0) activemodel (>= 3.2.0) activesupport (>= 3.2.0) - celluloid (0.13.0) + celluloid (0.14.0) timers (>= 1.0.0) charlock_holmes (0.6.9.4) chosen-rails (0.9.8) @@ -103,10 +93,10 @@ GEM thor crack (0.3.2) daemons (1.1.9) - database_cleaner (0.9.1) + database_cleaner (1.0.1) debug_inspector (0.0.2) descendants_tracker (0.0.1) - devise (2.2.3) + devise (2.2.4) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) railties (~> 3.1) @@ -130,6 +120,8 @@ GEM railties (>= 3.0.0) faraday (0.8.7) multipart-post (~> 1.1) + faraday_middleware (0.9.0) + faraday (>= 0.7.4, < 0.9) faye-websocket (0.4.7) eventmachine (>= 0.12.0) ffaker (1.16.0) @@ -201,7 +193,7 @@ GEM lumberjack (>= 1.0.2) pry (>= 0.9.10) thor (>= 0.14.6) - guard-rspec (2.6.0) + guard-rspec (3.0.0) guard (>= 1.8) rspec (~> 2.13) guard-spinach (0.0.2) @@ -214,8 +206,10 @@ GEM activesupport (>= 3.1, < 4.1) haml (>= 3.1, < 4.1) railties (>= 3.1, < 4.1) - hashie (2.0.4) + hashie (1.2.0) hike (1.2.2) + hipchat (0.9.0) + httparty http_parser.rb (0.5.3) httparty (0.11.0) multi_json (~> 1.0) @@ -244,7 +238,7 @@ GEM letter_opener (1.1.0) launchy (~> 2.2.0) libv8 (3.11.8.17) - listen (1.0.3) + listen (1.1.3) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) rb-kqueue (>= 0.2) @@ -258,7 +252,7 @@ GEM minitest (4.7.4) modernizr (2.6.2) sprockets (~> 2.0) - multi_json (1.7.2) + multi_json (1.7.3) multi_xml (0.5.3) multipart-post (1.2.0) mysql2 (0.3.11) @@ -291,9 +285,13 @@ GEM omniauth-oauth (~> 1.0) orm_adapter (0.4.0) pg (0.15.1) + poltergeist (1.3.0) + capybara (~> 2.1.0) + faye-websocket (>= 0.4.4, < 0.5.0) + http_parser.rb (~> 0.5.3) polyglot (0.3.3) posix-spawn (0.3.6) - pry (0.9.12.1) + pry (0.9.12.2) coderay (~> 1.0.5) method_source (~> 0.8) slop (~> 3.4) @@ -363,7 +361,7 @@ GEM redis-activesupport (3.2.3) activesupport (~> 3.2.3) redis-store (~> 1.1.0) - redis-namespace (1.2.1) + redis-namespace (1.3.0) redis (~> 3.0.0) redis-rack (1.4.2) rack (~> 1.4.1) @@ -396,7 +394,7 @@ GEM rubyntlm (0.1.1) sanitize (2.0.3) nokogiri (>= 1.4.4, < 1.6) - sass (3.2.8) + sass (3.2.9) sass-rails (3.2.6) railties (~> 3.2.0) sass (>= 3.1.10) @@ -412,14 +410,15 @@ GEM thor (~> 0.14) settingslogic (2.0.9) sexp_processor (4.2.1) - shoulda-matchers (1.3.0) + shoulda-matchers (2.1.0) activesupport (>= 3.0.0) - sidekiq (2.11.1) - celluloid (~> 0.13.0) - connection_pool (~> 1.0) - multi_json (~> 1) - redis (~> 3) + sidekiq (2.12.0) + celluloid (>= 0.14.0) + connection_pool (>= 1.0.0) + json + redis (>= 3.0) redis-namespace + simple_oauth (0.1.9) simplecov (0.7.1) multi_json (~> 1.0) simplecov-html (~> 0.7.1) @@ -432,7 +431,7 @@ GEM slim (1.3.8) temple (~> 0.6.3) tilt (~> 1.3.3) - slop (3.4.4) + slop (3.4.5) spinach (0.8.2) colorize (= 0.5.8) gherkin-ruby (~> 0.3.0) @@ -461,11 +460,24 @@ GEM thor (0.18.1) tilt (1.3.7) timers (1.1.0) + tinder (1.9.2) + eventmachine (~> 1.0) + faraday (~> 0.8) + faraday_middleware (~> 0.9) + hashie (~> 1.0) + json (~> 1.7.5) + mime-types (~> 1.19) + multi_json (~> 1.5) + twitter-stream (~> 0.1) treetop (1.4.12) polyglot polyglot (>= 0.3.1) turbolinks (1.1.1) coffee-rails + twitter-stream (0.1.16) + eventmachine (>= 0.12.8) + http_parser.rb (~> 0.5.1) + simple_oauth (~> 0.1.4) tzinfo (0.3.37) uglifier (2.0.1) execjs (>= 0.3.0) @@ -491,7 +503,7 @@ DEPENDENCIES awesome_print better_errors binding_of_caller - bootstrap-sass (= 2.2.1.1) + bootstrap-sass capybara carrierwave chosen-rails (= 0.9.8) @@ -522,6 +534,7 @@ DEPENDENCIES guard-rspec guard-spinach haml-rails + hipchat (~> 0.9.0) httparty jquery-atwho-rails (= 0.3.0) jquery-rails (= 2.1.3) @@ -538,7 +551,7 @@ DEPENDENCIES omniauth-google-oauth2 omniauth-twitter pg - poltergeist! + poltergeist (~> 1.3.0) pry puma (~> 2.0.1) quiet_assets (~> 1.0.1) @@ -557,7 +570,7 @@ DEPENDENCIES seed-fu select2-rails settingslogic - shoulda-matchers (= 1.3.0) + shoulda-matchers (~> 2.1.0) sidekiq simplecov sinatra @@ -570,6 +583,7 @@ DEPENDENCIES test_after_commit therubyracer thin + tinder (~> 1.9.2) turbolinks uglifier webmock diff --git a/README.md b/README.md index 7f0ae041ead..2d8113c03b1 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,9 @@ ### 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.org community site: [Homepage](http://gitlab.org) | [Screenshots](http://gitlab.org/screenshots/) | [Blog](http://blog.gitlab.org/) | [Demo](http://demo.gitlabhq.com/users/sign_in) -* GitLab.com commercial services: [Homepage](http://www.gitlab.com/) [Subscription](http://www.gitlab.com/subscription/) [Consultancy](http://www.gitlab.com/consultancy/) [GitLab Cloud](http://www.gitlab.com/cloud/) [Blog](http://blog.gitlab.com/) +* GitLab.com commercial services: [Homepage](http://www.gitlab.com/) | [Subscription](http://www.gitlab.com/subscription/) | [Consultancy](http://www.gitlab.com/consultancy/) | [GitLab Cloud](http://www.gitlab.com/cloud/) | [Blog](http://blog.gitlab.com/) * GitLab CI: [Readme](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) of the GitLab open-source continuous integration server @@ -43,13 +43,13 @@ * gitlab-shell * redis -** More details are in the [requirements doc](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/requirements.md) +** More details are in the [requirements doc](doc/install/requirements.md) ### Installation #### Official production installation -* [Installation guide for a production server](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md) +* [Installation guide for a production server](doc/install/installation.md) #### Official development installation @@ -72,18 +72,18 @@ If you want to contribute, please first read our [Contributing Guidelines](https ### New versions and upgrading -Each month on the 22th a new version is released together with an upgrade guide. +Each month on the 22nd a new version is released together with an upgrade guide. -* [Upgrade guides](https://github.com/gitlabhq/gitlabhq/tree/master/doc/update) +* [Upgrade guides](doc/update) -* [Changelog](https://github.com/gitlabhq/gitlabhq/blob/master/CHANGELOG) +* [Changelog](CHANGELOG) * Features that will be in the next release are listed on [the feedback and suggestions forum with the status "started"](http://feedback.gitlab.com/forums/176466-general/status/796456). ### Run in production mode -1. The Installation guide contains instructions on how to download an init script and run it automatically on boot. You can also start the init script manually: + The Installation guide contains instructions on how to download an init script and run it automatically on boot. You can also start the init script manually: sudo service gitlab start @@ -124,13 +124,13 @@ Start it with [Foreman](https://github.com/ddollar/foreman) ### GitLab interfaces -* [GitLab API](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/README.md) +* [GitLab API](doc/api/README.md) -* [Rake tasks](https://github.com/gitlabhq/gitlabhq/tree/master/doc/raketasks) +* [Rake tasks](doc/raketasks) -* [Directory structure](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/structure.md) +* [Directory structure](doc/install/structure.md) -* [Databases](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/databases.md) +* [Databases](doc/install/databases.md) ### Getting help @@ -139,7 +139,7 @@ Start it with [Foreman](https://github.com/ddollar/foreman) * [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) contains solutions to common problems. -* [Support forum](https://groups.google.com/forum/#!forum/gitlabhq) is the best place to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and had it resolved. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix. +* [Support forum](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix. * [Feedback and suggestions forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab. @@ -1 +1 @@ -5.2.0.rc1 +5.2.0 diff --git a/app/assets/javascripts/main.js.coffee b/app/assets/javascripts/main.js.coffee index 45ef44fcc04..65005d6f617 100644 --- a/app/assets/javascripts/main.js.coffee +++ b/app/assets/javascripts/main.js.coffee @@ -41,6 +41,9 @@ window.linkify = (str) -> exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig return str.replace(exp,"<a href='$1'>$1</a>") +window.simpleFormat = (str) -> + linkify(sanitize(str).replace(/\n/g, '<br />')) + window.startSpinner = -> $('.turbolink-spinner').fadeIn() diff --git a/app/assets/javascripts/wall.js.coffee b/app/assets/javascripts/wall.js.coffee index c8fc960e174..4f71e6e0c35 100644 --- a/app/assets/javascripts/wall.js.coffee +++ b/app/assets/javascripts/wall.js.coffee @@ -60,8 +60,8 @@ class Wall renderNote: (note) -> template = @noteTemplate() template = template.replace('{{author_name}}', note.author.name) - template = template.replace('{{created_at}}', note.created_at) - template = template.replace('{{text}}', linkify(sanitize(note.body))) + template = template.replace(/{{created_at}}/g, note.created_at) + template = template.replace('{{text}}', simpleFormat(note.body)) if note.attachment file = '<i class="icon-paper-clip"/><a href="/files/note/' + note.id + '/' + note.attachment + '">' + note.attachment + '</a>' diff --git a/app/assets/stylesheets/gitlab_bootstrap/files.scss b/app/assets/stylesheets/gitlab_bootstrap/files.scss index d0bf3bdd6d3..78a3f0b810d 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/files.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/files.scss @@ -49,6 +49,15 @@ &.wiki { padding: 20px; font-size: 13px; + + .highlight { + margin-bottom: 9px; + @include border-radius(4px); + + > pre { + margin: 0; + } + } } &.blob_file { diff --git a/app/assets/stylesheets/gitlab_bootstrap/typography.scss b/app/assets/stylesheets/gitlab_bootstrap/typography.scss index 1f0c4802318..ab14624186d 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/typography.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/typography.scss @@ -41,6 +41,10 @@ a { color: $primary_color; } + &:focus { + text-decoration: underline; + } + &.btn { color: $style_color; &:hover { diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index dfc1fdcee8a..d156166503f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -69,7 +69,7 @@ class ApplicationController < ActionController::Base @project else @project = nil - render_404 + render_404 and return end end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index f0d69f11184..686edd8af80 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -2,6 +2,9 @@ class ProfilesController < ApplicationController include ActionView::Helpers::SanitizeHelper before_filter :user + before_filter :authorize_change_password!, only: :update_password + before_filter :authorize_change_username!, only: :update_username + layout 'profile' def show @@ -53,9 +56,7 @@ class ProfilesController < ApplicationController end def update_username - if @user.can_change_username? - @user.update_attributes(username: params[:user][:username]) - end + @user.update_attributes(username: params[:user][:username]) respond_to do |format| format.js @@ -80,4 +81,12 @@ class ProfilesController < ApplicationController user_attributes end + + def authorize_change_password! + return render_404 if @user.ldap_user? + end + + def authorize_change_username! + return render_404 unless @user.can_change_username? + end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 0e05213b797..e202ed3234e 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -33,12 +33,12 @@ class ProjectsController < ProjectResourceController end def update - status = ::Projects::UpdateContext.new(project, current_user, params).execute + status = ::Projects::UpdateContext.new(@project, current_user, params).execute respond_to do |format| if status flash[:notice] = 'Project was successfully updated.' - format.html { redirect_to edit_project_path(project), notice: 'Project was successfully updated.' } + format.html { redirect_to edit_project_path(@project), notice: 'Project was successfully updated.' } format.js else format.html { render action: "edit" } diff --git a/app/controllers/services_controller.rb b/app/controllers/services_controller.rb index 25a06501e07..fcfc4c84a91 100644 --- a/app/controllers/services_controller.rb +++ b/app/controllers/services_controller.rb @@ -1,25 +1,21 @@ class ServicesController < ProjectResourceController # Authorize before_filter :authorize_admin_project! + before_filter :service, only: [:edit, :update, :test] respond_to :html def index - @gitlab_ci_service = @project.gitlab_ci_service + @project.build_missing_services + @services = @project.services.reload end def edit - @service = @project.gitlab_ci_service - - # Create if missing - @service = @project.create_gitlab_ci_service unless @service end def update - @service = @project.gitlab_ci_service - if @service.update_attributes(params[:service]) - redirect_to edit_project_service_path(@project, :gitlab_ci) + redirect_to edit_project_service_path(@project, @service.to_param) else render 'edit' end @@ -28,9 +24,14 @@ class ServicesController < ProjectResourceController def test data = GitPushService.new.sample_data(project, current_user) - @service = project.gitlab_ci_service @service.execute(data) redirect_to :back end + + private + + def service + @service ||= @project.services.find { |service| service.to_param == params[:id] } + end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ef90a8877ee..65d03fe0ccd 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -119,10 +119,6 @@ module ApplicationHelper Emoji.names.to_s end - def ldap_enable? - Devise.omniauth_providers.include?(:ldap) - end - def app_theme Gitlab::Theme.css_class_by_id(current_user.try(:theme_id)) end diff --git a/app/helpers/oauth_helper.rb b/app/helpers/oauth_helper.rb new file mode 100644 index 00000000000..017c22d9e1f --- /dev/null +++ b/app/helpers/oauth_helper.rb @@ -0,0 +1,13 @@ +module OauthHelper + def ldap_enabled? + Devise.omniauth_providers.include?(:ldap) + end + + def default_providers + [:twitter, :github, :google_oauth2, :ldap] + end + + def enabled_oauth_providers + Devise.omniauth_providers + end +end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 1db8b7c689c..9b142714980 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -44,4 +44,8 @@ module ProjectsHelper project.name end end + + def remove_project_message(project) + "You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?" + end end diff --git a/app/helpers/user_teams_helper.rb b/app/helpers/user_teams_helper.rb index 2055bb3c8bc..8603ee434a8 100644 --- a/app/helpers/user_teams_helper.rb +++ b/app/helpers/user_teams_helper.rb @@ -22,5 +22,4 @@ module UserTeamsHelper def remove_from_user_team_message(team, member) "You are going to remove #{member.name} from #{team.name}. Are you sure?" end - end diff --git a/app/models/campfire_service.rb b/app/models/campfire_service.rb new file mode 100644 index 00000000000..6450ffe7318 --- /dev/null +++ b/app/models/campfire_service.rb @@ -0,0 +1,76 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# token :string(255) +# project_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean default(FALSE), not null +# project_url :string(255) +# + +class CampfireService < Service + attr_accessible :subdomain, :room + + validates :token, presence: true, if: :activated? + + def title + 'Campfire' + end + + def description + 'Simple web-based real-time group chat' + end + + def to_param + 'campfire' + end + + def fields + [ + { type: 'text', name: 'token', placeholder: '' }, + { type: 'text', name: 'subdomain', placeholder: '' }, + { type: 'text', name: 'room', placeholder: '' } + ] + end + + def execute(push_data) + room = gate.find_room_by_name(self.room) + return true unless room + + message = build_message(push_data) + + room.speak(message) + end + + private + + def gate + @gate ||= Tinder::Campfire.new(subdomain, token: token) + end + + def build_message(push) + ref = push[:ref].gsub("refs/heads/", "") + before = push[:before] + after = push[:after] + + message = "" + message << "[#{project.name_with_namespace}] " + message << "#{push[:user_name]} " + + if before =~ /000000/ + message << "pushed new branch #{ref} \n" + elsif after =~ /000000/ + message << "removed branch #{ref} \n" + else + message << "pushed #{push[:total_commits_count]} commits to #{ref}. " + message << "#{project.web_url}/compare/#{before}...#{after}" + end + + message + end +end diff --git a/app/models/gitlab_ci_service.rb b/app/models/gitlab_ci_service.rb index 9b1c707a6c9..bdbe7724be0 100644 --- a/app/models/gitlab_ci_service.rb +++ b/app/models/gitlab_ci_service.rb @@ -54,4 +54,23 @@ class GitlabCiService < Service def status_img_path project_url + "/status.png?ref=" + project.default_branch end + + def title + 'GitLab CI' + end + + def description + 'Continuous integration server from GitLab' + end + + def to_param + 'gitlab_ci' + end + + def fields + [ + { type: 'text', name: 'token', placeholder: 'GitLab CI project specific token' }, + { type: 'text', name: 'project_url', placeholder: 'http://ci.gitlabhq.com/projects/3'} + ] + end end diff --git a/app/models/hipchat_service.rb b/app/models/hipchat_service.rb new file mode 100644 index 00000000000..13429fa83b4 --- /dev/null +++ b/app/models/hipchat_service.rb @@ -0,0 +1,73 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# token :string(255) +# project_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean default(FALSE), not null +# project_url :string(255) +# + +class HipchatService < Service + attr_accessible :room + + validates :token, presence: true, if: :activated? + + def title + 'Hipchat' + end + + def description + 'Simple web-based real-time group chat' + end + + def to_param + 'hipchat' + end + + def fields + [ + { type: 'text', name: 'token', placeholder: '' }, + { type: 'text', name: 'room', placeholder: '' } + ] + end + + def execute(push_data) + gate[room].send('Gitlab', create_message(push_data)) + end + + private + + def gate + @gate ||= HipChat::Client.new(token) + end + + def create_message(push) + ref = push[:ref].gsub("refs/heads/", "") + before = push[:before] + after = push[:after] + + message = "" + message << "#{push[:user_name]} " + if before =~ /000000/ + message << "pushed new branch <a href=\"#{project.web_url}/commits/#{ref}\">#{ref}</a> to <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a>\n" + elsif after =~ /000000/ + message << "removed branch #{ref} from <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> \n" + else + message << "#pushed to branch <a href=\"#{project.web_url}/commits/#{ref}\">#{ref}</a> " + message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> " + message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)" + for commit in push[:commits] do + message << "<br /> - #{commit[:message]} (<a href=\"#{commit[:url]}\">#{commit[:id][0..5]}</a>)" + end + end + + message + end + +end
\ No newline at end of file diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index c1b4d4e0760..b2ad1b76f1f 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -118,7 +118,7 @@ class MergeRequest < ActiveRecord::Base end def broken_diffs? - diffs == [Gitlab::Git::Diff::BROKEN_DIFF] + diffs == broken_diffs end def valid_diffs? @@ -214,10 +214,22 @@ class MergeRequest < ActiveRecord::Base end def dump_diffs(diffs) - diffs.map(&:to_hash) + if diffs == broken_diffs + broken_diffs + elsif diffs.respond_to?(:map) + diffs.map(&:to_hash) + end + end + + def load_diffs(raw) + if raw == broken_diffs + broken_diffs + elsif raw.respond_to?(:map) + raw.map { |hash| Gitlab::Git::Diff.new(hash) } + end end - def load_diffs(array) - array.map { |hash| Gitlab::Git::Diff.new(hash) } + def broken_diffs + [Gitlab::Git::Diff::BROKEN_DIFF] end end diff --git a/app/models/project.rb b/app/models/project.rb index bd33e3a4c13..9147aed3d40 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -45,9 +45,12 @@ class Project < ActiveRecord::Base has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id' has_one :gitlab_ci_service, dependent: :destroy + has_one :campfire_service, dependent: :destroy + has_one :hipchat_service, dependent: :destroy has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_from_project, through: :forked_project_link + has_many :services, dependent: :destroy has_many :events, dependent: :destroy has_many :merge_requests, dependent: :destroy has_many :issues, dependent: :destroy, order: "state DESC, created_at DESC" @@ -223,8 +226,18 @@ class Project < ActiveRecord::Base self.issues_enabled && !self.used_default_issues_tracker? end - def services - [gitlab_ci_service].compact + def build_missing_services + available_services_names.each do |service_name| + service = services.find { |service| service.to_param == service_name } + + # If service is available but missing in db + # we should create an instance. Ex `create_gitlab_ci_service` + service = self.send :"create_#{service_name}_service" if service.nil? + end + end + + def available_services_names + %w(gitlab_ci campfire hipchat) end def gitlab_ci? @@ -410,4 +423,28 @@ class Project < ActiveRecord::Base def forked? !(forked_project_link.nil? || forked_project_link.forked_from_project.nil?) end + + def rename_repo + old_path_with_namespace = File.join(namespace_dir, path_was) + new_path_with_namespace = File.join(namespace_dir, path) + + if gitlab_shell.mv_repository(old_path_with_namespace, new_path_with_namespace) + # If repository moved successfully we need to remove old satellite + # and send update instructions to users. + # However we cannot allow rollback since we moved repository + # So we basically we mute exceptions in next actions + begin + gitlab_shell.rm_satellites(old_path_with_namespace) + send_move_instructions + rescue + # Returning false does not rolback after_* transaction but gives + # us information about failing some of tasks + false + end + else + # if we cannot move namespace directory we should rollback + # db changes in order to prevent out of sync between db and fs + raise Exception.new('repository cannot be renamed') + end + end end diff --git a/app/models/service.rb b/app/models/service.rb index d3486d29200..3e945aa898c 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -13,6 +13,8 @@ # project_url :string(255) # +# To add new service you should build a class inherited from Service +# and implement a set of methods class Service < ActiveRecord::Base attr_accessible :title, :token, :type, :active @@ -24,4 +26,25 @@ class Service < ActiveRecord::Base def activated? active end + + def title + # implement inside child + end + + def description + # implement inside child + end + + def to_param + # implement inside child + end + + def fields + # implement inside child + [] + end + + def execute + # implement inside child + end end diff --git a/app/models/user.rb b/app/models/user.rb index 55aa5b563c5..255a5ebd2a9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -340,4 +340,8 @@ class User < ActiveRecord::Base nil end end + + def ldap_user? + extern_uid && provider == 'ldap' + end end diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb index de9edf41c6d..dda7be625da 100644 --- a/app/observers/project_observer.rb +++ b/app/observers/project_observer.rb @@ -12,6 +12,7 @@ class ProjectObserver < BaseObserver def after_update(project) project.send_move_instructions if project.namespace_id_changed? + project.rename_repo if project.path_changed? end def after_destroy(project) diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 132bb14a675..a52d13d5237 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -26,6 +26,7 @@ class SystemHooksService data.merge!({ name: model.name, path: model.path, + path_with_namespace: model.path_with_namespace, project_id: model.id, owner_name: model.owner.name, owner_email: model.owner.email diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index aa006dfc997..59831d83cc2 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -53,7 +53,7 @@ = link_to project.name_with_namespace, [:admin, project] .pull-right = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" - = link_to 'Destroy', [project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" + = link_to 'Destroy', [project], confirm: remove_project_message(project), method: :delete, class: "btn btn-small btn-remove" - if @projects.blank? %p.nothing_here_message 0 projects matches = paginate @projects, theme: "gitlab" diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml new file mode 100644 index 00000000000..1ca43d5dd08 --- /dev/null +++ b/app/views/devise/sessions/_new_base.html.haml @@ -0,0 +1,13 @@ += form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| + = f.text_field :login, class: "text top", placeholder: "Username or Email", autofocus: "autofocus" + = f.password_field :password, class: "text bottom", placeholder: "Password" + - if devise_mapping.rememberable? + .clearfix.inputs-list + %label.checkbox.remember_me{for: "user_remember_me"} + = f.check_box :remember_me + %span Remember me + = f.submit "Sign in", class: "btn-create btn" + .pull-right + = link_to "Forgot your password?", new_password_path(resource_name), class: "btn" + + diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml index 29ba9c1e99c..575d33949b6 100644 --- a/app/views/devise/sessions/_new_ldap.html.haml +++ b/app/views/devise/sessions/_new_ldap.html.haml @@ -1,29 +1,5 @@ -= form_tag(user_omniauth_callback_path(:ldap), class: "login-box", id: 'new_ldap_user' ) do - = image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo" += form_tag(user_omniauth_callback_path(:ldap), id: 'new_ldap_user' ) do = text_field_tag :username, nil, {class: "text top", placeholder: "LDAP Login", autofocus: "autofocus"} = password_field_tag :password, nil, {class: "text bottom", placeholder: "Password"} %br/ - = submit_tag "LDAP Sign in", class: "btn-primary btn" - - if devise_mapping.omniauthable? - - (resource_class.omniauth_providers - [:ldap]).each do |provider| - %hr/ - = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), class: "btn btn-primary" - %br/ - %hr/ - %a#other_form_toggle{href: "#", onclick: "javascript:$('#new_user').toggle();"} Other Sign in - :javascript - $(function() { - $('#new_user').toggle(); - }); -= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: "login-box" }) do |f| - = f.text_field :login, class: "text top", placeholder: "Username or Email", autofocus: "autofocus" - = f.password_field :password, class: "text bottom", placeholder: "Password" - - if devise_mapping.rememberable? - .clearfix.inputs-list - %label.checkbox.remember_me{for: "user_remember_me"} - = f.check_box :remember_me - %span Remember me - %br/ - = f.submit "Sign in", class: "btn-primary btn" - .pull-right - = render partial: "devise/shared/links" + = submit_tag "LDAP Sign in", class: "btn-create btn" diff --git a/app/views/devise/sessions/_oauth_providers.html.haml b/app/views/devise/sessions/_oauth_providers.html.haml new file mode 100644 index 00000000000..710a5d52514 --- /dev/null +++ b/app/views/devise/sessions/_oauth_providers.html.haml @@ -0,0 +1,10 @@ +- if enabled_oauth_providers.present? + %hr + %div{:'data-no-turbolink' => 'data-no-turbolink'} + %span Sign in with: + - (enabled_oauth_providers - [:ldap]).each do |provider| + %span + - if default_providers.include?(provider) + = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) + - else + = link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn" diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index baa232350a5..8b71ebed5f4 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -1,32 +1,31 @@ -- if ldap_enable? - = render partial: 'devise/sessions/new_ldap' -- else - = form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: "login-box" }) do |f| - = image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo" - = f.text_field :login, class: "text top", placeholder: "Username or Email", autofocus: "autofocus" - = f.password_field :password, class: "text bottom", placeholder: "Password" - - if devise_mapping.rememberable? - .clearfix.inputs-list - %label.checkbox.remember_me{for: "user_remember_me"} - = f.check_box :remember_me - %span Remember me - %br/ - = f.submit "Sign in", class: "btn-create btn" - .pull-right - = link_to "Forgot your password?", new_password_path(resource_name), class: "btn" - %br/ - - if Gitlab.config.gitlab.signup_enabled - %hr/ +.login-box + = image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo" + + - if ldap_enabled? + %ul.nav.nav-tabs + %li.active + = link_to 'LDAP', '#tab-ldap', 'data-toggle' => 'tab' + %li + = link_to 'Ordinary', '#tab-signin', 'data-toggle' => 'tab' + .tab-content + %div#tab-ldap.tab-pane.active + = render partial: 'devise/sessions/new_ldap' + %div#tab-signin.tab-pane + = render partial: 'devise/sessions/new_base' + + - else + = render partial: 'devise/sessions/new_base' + + + = render 'devise/sessions/oauth_providers' if devise_mapping.omniauthable? + + - if Gitlab.config.gitlab.signup_enabled + %hr + %div Don't have an account? - = link_to "Sign up", new_registration_path(resource_name) - - if devise_mapping.omniauthable? && resource_class.omniauth_providers.present? - %hr - %div - %span Sign in with: - - resource_class.omniauth_providers.each do |provider| - %span - = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) + %strong + = link_to "Sign up", new_registration_path(resource_name) - - if extra_config.has_key?('sign_in_text') - %hr - = markdown(extra_config.sign_in_text) + - if extra_config.has_key?('sign_in_text') + %hr + = markdown(extra_config.sign_in_text) diff --git a/app/views/edit_tree/show.html.haml b/app/views/edit_tree/show.html.haml index 509205436ea..94a577d8e41 100644 --- a/app/views/edit_tree/show.html.haml +++ b/app/views/edit_tree/show.html.haml @@ -27,7 +27,7 @@ .message to branch %strong= @ref - = link_to "Cancel", project_tree_path(@project, @id), class: "btn btn-cancel", confirm: "Are you sure?" + = link_to "Cancel", project_blob_path(@project, @id), class: "btn btn-cancel", confirm: "Are you sure?" :javascript ace.config.set("modePath", "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict") diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 7bda5cb6027..1685c6eec53 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -27,6 +27,8 @@ %li Ask in our = link_to "support forum", "https://groups.google.com/forum/#!forum/gitlabhq" + or on + = link_to "Stack Overflow", "http://stackoverflow.com/questions/tagged/gitlab" %li Browse our = link_to "issue tracker", "https://github.com/gitlabhq/gitlabhq/issues" diff --git a/app/views/layouts/_google_analytics.html.haml b/app/views/layouts/_google_analytics.html.haml index 1452651e1c0..81e03c7eff2 100644 --- a/app/views/layouts/_google_analytics.html.haml +++ b/app/views/layouts/_google_analytics.html.haml @@ -1,6 +1,6 @@ :javascript var _gaq = _gaq || []; - _gaq.push(['_setAccount', '#{gitlab_config.google_analytics_id}']); + _gaq.push(['_setAccount', '#{extra_config.google_analytics_id}']); _gaq.push(['_trackPageview']); (function() { diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 637b2987ff8..0775abea3dd 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -9,7 +9,7 @@ = csrf_meta_tags = include_gon - = render 'layouts/google_analytics' if gitlab_config.has_key?('google_analytics_id') + = render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id') -# Atom feed - if current_user diff --git a/app/views/merge_requests/show/_diffs.html.haml b/app/views/merge_requests/show/_diffs.html.haml index 0807454c4b0..033d66a4ad4 100644 --- a/app/views/merge_requests/show/_diffs.html.haml +++ b/app/views/merge_requests/show/_diffs.html.haml @@ -4,7 +4,7 @@ %h4.nothing_here_message Can't load diff. You can - = link_to "download it", project_merge_request_path(@project, @merge_request), format: :diff, class: "vlink" + = link_to "download it", project_merge_request_path(@project, @merge_request, format: :diff), class: "vlink" instead. - else %h4.nothing_here_message Nothing to merge diff --git a/app/views/milestones/index.html.haml b/app/views/milestones/index.html.haml index 89892cd23f1..fb7cbc41edf 100644 --- a/app/views/milestones/index.html.haml +++ b/app/views/milestones/index.html.haml @@ -21,12 +21,12 @@ = link_to project_milestones_path(@project, f: "all") do All .span9 - %div.ui-box + .ui-box %ul.well-list = render @milestones - - if @milestones.present? - %li.bottom= paginate @milestones, theme: "gitlab" - - else + - if @milestones.blank? %li %h3.nothing_here_message Nothing to show here + + = paginate @milestones, theme: "gitlab" diff --git a/app/views/profiles/account.html.haml b/app/views/profiles/account.html.haml index 16d26c0d8e1..9bba73a080a 100644 --- a/app/views/profiles/account.html.haml +++ b/app/views/profiles/account.html.haml @@ -1,11 +1,35 @@ -- if Gitlab.config.omniauth.enabled - %fieldset - %legend Social Accounts - .oauth_select_holder - %p.hint Tip: Click on icon to activate sigin with one of the following services - - User.omniauth_providers.each do |provider| - %span{class: oauth_active_class(provider) } - = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) +- unless current_user.ldap_user? + - if Gitlab.config.omniauth.enabled + %fieldset + %legend Social Accounts + .oauth_select_holder + %p.hint Tip: Click on icon to activate sigin with one of the following services + - User.omniauth_providers.each do |provider| + %span{class: oauth_active_class(provider) } + = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) + + + %fieldset.update-password + %legend Password + = form_for @user, url: update_password_profile_path, method: :put do |f| + .padded + %p.slead After successful password update you will be redirected to login page where you should login with new password + -if @user.errors.any? + .alert.alert-error + %ul + - @user.errors.full_messages.each do |msg| + %li= msg + + .clearfix + = f.label :password + .input= f.password_field :password, required: true + .clearfix + = f.label :password_confirmation + .input + = f.password_field :password_confirmation, required: true + .clearfix + .input + = f.submit 'Save password', class: "btn btn-save" @@ -29,29 +53,6 @@ %span You don`t have one yet. Click generate to fix it. = f.submit 'Generate', class: "btn success btn-build-token" -%fieldset.update-password - %legend Password - = form_for @user, url: update_password_profile_path, method: :put do |f| - .padded - %p.slead After successful password update you will be redirected to login page where you should login with new password - -if @user.errors.any? - .alert.alert-error - %ul - - @user.errors.full_messages.each do |msg| - %li= msg - - .clearfix - = f.label :password - .input= f.password_field :password, required: true - .clearfix - = f.label :password_confirmation - .input - = f.password_field :password_confirmation, required: true - .clearfix - .input - = f.submit 'Save password', class: "btn btn-save" - - - if current_user.can_change_username? %fieldset.update-username diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index dd087100e4c..bec64bf6c58 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -11,6 +11,8 @@ %li.active = link_to 'Settings', '#tab-settings', 'data-toggle' => 'tab' %li + = link_to 'Rename repo', '#tab-rename', 'data-toggle' => 'tab' + %li = link_to 'Transfer', '#tab-transfer', 'data-toggle' => 'tab' %li = link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab' @@ -137,6 +139,24 @@ - else %p.nothing_here_message Only project owner can transfer a project + .tab-pane#tab-rename + .ui-box.ui-box-danger + %h5.title Rename repository + .errors-holder + .form-holder + = form_for(@project) do |f| + .control-group + = f.label :path do + %span Path + .controls + .clearfix + = f.text_field :path + %ul + %li Be careful. Rename of project repo can have unintended side effects + %li You will need to update your local repositories to point to the new location. + .form-actions + = f.submit 'Rename', class: "btn btn-remove" + .tab-pane#tab-remove - if can?(current_user, :remove_project, @project) .ui-box.ui-box-danger @@ -147,7 +167,7 @@ %p %strong Removed project can not be restored! - = link_to 'Remove project', @project, confirm: 'Removed project can not be restored! Are you sure?', method: :delete, class: "btn btn-remove btn-small" + = link_to 'Remove project', @project, confirm: remove_project_message(@project), method: :delete, class: "btn btn-remove btn-small" - else %p.nothing_here_message Only project owner can remove a project diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index b1795b301b0..56dbbf0755e 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -31,4 +31,4 @@ - if can? current_user, :remove_project, @project .prepend-top-20 - = link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn btn-remove pull-right" + = link_to 'Remove project', @project, confirm: remove_project_message(@project), method: :delete, class: "btn btn-remove pull-right" diff --git a/app/views/services/_form.html.haml b/app/views/services/_form.html.haml new file mode 100644 index 00000000000..ff6769531c4 --- /dev/null +++ b/app/views/services/_form.html.haml @@ -0,0 +1,48 @@ +%h3.page_title + - if @service.activated? + %span.cgreen + %i.icon-circle + - else + %span.cgray + %i.icon-circle-blank + = @service.title + +%p= @service.description + +.back_link + = link_to project_services_path(@project) do + ← to services + +%hr + += form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put) do |f| + - if @service.errors.any? + .alert.alert-error + %ul + - @service.errors.full_messages.each do |msg| + %li= msg + + + .control-group + = f.label :active, "Active", class: "control-label" + .controls + = f.check_box :active + + - @service.fields.each do |field| + - name = field[:name] + - type = field[:type] + - placeholder = field[:placeholder] + + .control-group + = f.label name, class: "control-label" + .controls + - if type == 'text' + = f.text_field name, class: "input-xlarge", placeholder: placeholder + - elsif type == 'checkbox' + = f.check_box name + + .form-actions + = f.submit 'Save', class: 'btn btn-save' + + - if @service.valid? && @service.activated? + = link_to 'Test settings', test_project_service_path(@project, @service.to_param), class: 'btn btn-small' diff --git a/app/views/services/_gitlab_ci.html.haml b/app/views/services/_gitlab_ci.html.haml deleted file mode 100644 index dfde643849e..00000000000 --- a/app/views/services/_gitlab_ci.html.haml +++ /dev/null @@ -1,46 +0,0 @@ -%h3.page_title - GitLab CI - %small Continuous integration server from GitLab - .pull-right - - if @service.active - %small.cgreen Enabled - - else - %small.cgray Disabled - - - -.back_link - = link_to project_services_path(@project) do - ← to services - -%hr -= form_for(@service, :as => :service, :url => project_service_path(@project, :gitlab_ci), :method => :put) do |f| - - if @service.errors.any? - .alert.alert-error - %ul - - @service.errors.full_messages.each do |msg| - %li= msg - - - .control-group - = f.label :active, "Active", class: "control-label" - .controls - = f.check_box :active - - .control-group - = f.label :project_url, "Project URL", class: "control-label" - .controls - = f.text_field :project_url, class: "input-xlarge", placeholder: "http://ci.gitlabhq.com/projects/3" - - .control-group - = f.label :token, class: "control-label" do - CI Project token - .controls - = f.text_field :token, class: "input-xlarge", placeholder: "GitLab CI project specific token" - - - .form-actions - = f.submit 'Save', class: 'btn btn-save' - - - if @service.valid? && @service.active - = link_to 'Test settings', test_project_service_path(@project), class: 'btn btn-small' diff --git a/app/views/services/edit.html.haml b/app/views/services/edit.html.haml index 0c63a7ed58c..d4bc9e41c27 100644 --- a/app/views/services/edit.html.haml +++ b/app/views/services/edit.html.haml @@ -1,3 +1,3 @@ = render "projects/settings_nav" -= render 'gitlab_ci' += render 'form' diff --git a/app/views/services/index.html.haml b/app/views/services/index.html.haml index eb2f8d0ca1c..bd52948a6fd 100644 --- a/app/views/services/index.html.haml +++ b/app/views/services/index.html.haml @@ -3,30 +3,16 @@ %h3.page_title Services %br -%ul.ui-box.well-list - %li - %h4.cgreen - = link_to edit_project_service_path(@project, :gitlab_ci) do - GitLab CI - %small Continuous integration server from GitLab - .pull-right - - if @gitlab_ci_service.try(:active) - %small.cgreen - %i.icon-ok - Enabled +%ul.bordered-list + - @services.each do |service| + %li + %h4 + - if service.activated? + %span.cgreen + %i.icon-circle - else - %small.cgray - %i.icon-off - Disabled - %li.disabled - %h4 - Jenkins CI - %small An extendable open source continuous integration server - .pull-right - %small Not implemented yet - %li.disabled - %h4 - Campfire - %small Web-based group chat tool - .pull-right - %small Not implemented yet + %span.cgray + %i.icon-circle-blank + = link_to edit_project_service_path(@project, service.to_param) do + = service.title + %p= service.description diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 19b58bcc371..413560159d5 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -34,6 +34,8 @@ production: &base ## Project settings default_projects_limit: 10 + + ## Users management # 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 diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb index 6d3a9f07787..16d1d4a9fdd 100644 --- a/config/initializers/secret_token.rb +++ b/config/initializers/secret_token.rb @@ -1,7 +1,23 @@ # Be sure to restart your server when you modify this file. +require 'securerandom' + # Your secret key for verifying the integrity of signed cookies. # If you change this key, all old signed cookies will become invalid! # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. -Gitlab::Application.config.secret_token = '0a38e9a40ca5d66d7002a6ade0ed0f8b71058c820163f66cf65d91521ab55255ff708b9909b138008a7f13d68fec575def1dc3ff7200cd72b065896315e0bed2' + +def find_secure_token + token_file = Rails.root.join('.secret') + if File.exist? token_file + # Use the existing token. + File.read(token_file).chomp + else + # Generate a new token of 64 random hexadecimal characters and store it in token_file. + token = SecureRandom.hex(64) + File.write(token_file, token) + token + end +end + +Gitlab::Application.config.secret_token = find_secure_token diff --git a/db/migrate/20130522141856_add_more_fields_to_service.rb b/db/migrate/20130522141856_add_more_fields_to_service.rb new file mode 100644 index 00000000000..298e902df2f --- /dev/null +++ b/db/migrate/20130522141856_add_more_fields_to_service.rb @@ -0,0 +1,6 @@ +class AddMoreFieldsToService < ActiveRecord::Migration + def change + add_column :services, :subdomain, :string + add_column :services, :room, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index d2fa70cc374..6a16caf59d8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130506095501) do +ActiveRecord::Schema.define(:version => 20130522141856) do create_table "deploy_keys_projects", :force => true do |t| t.integer "deploy_key_id", :null => false @@ -194,6 +194,8 @@ ActiveRecord::Schema.define(:version => 20130506095501) do t.datetime "updated_at", :null => false t.boolean "active", :default => false, :null => false t.string "project_url" + t.string "subdomain" + t.string "room" end add_index "services", ["project_id"], :name => "index_services_on_project_id" diff --git a/doc/api/README.md b/doc/api/README.md index c4d44c52748..16af64be049 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -69,13 +69,13 @@ When listing resources you can pass the following parameters: ## Contents -+ [Users](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/users.md) -+ [Session](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/session.md) -+ [Projects](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md) -+ [Groups](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/groups.md) -+ [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md) -+ [Repositories](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/repositories.md) -+ [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md) -+ [Milestones](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/milestones.md) -+ [Notes](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/notes.md) -+ [System Hooks](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/system_hooks.md) ++ [Users](doc/api/users.md) ++ [Session](doc/api/session.md) ++ [Projects](doc/api/projects.md) ++ [Groups](doc/api/groups.md) ++ [Snippets](doc/api/snippets.md) ++ [Repositories](doc/api/repositories.md) ++ [Issues](doc/api/issues.md) ++ [Milestones](doc/api/milestones.md) ++ [Notes](doc/api/notes.md) ++ [System Hooks](doc/api/system_hooks.md) diff --git a/doc/install/installation.md b/doc/install/installation.md index d6679222dd5..e770cf24934 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -1,3 +1,11 @@ +# Select Version to Install +Make sure you view this installation guide from the branch (version) of GitLab you would like to install. In most cases +this should be the highest numbered stable branch (example shown below). + +![capture](https://f.cloud.github.com/assets/1192780/564911/2f9f3e1e-c5b7-11e2-9f89-98e527d1adec.png) + +If this is unclear check the [GitLab Blog](http://blog.gitlab.org/) for installation guide links by version. + # Important notes This installation guide was created for and tested on **Debian/Ubuntu** operating systems. Please read [`doc/install/requirements.md`](./requirements.md) for hardware and operating system requirements. @@ -6,7 +14,7 @@ This is the official installation guide to set up a production server. To set up The following steps have been known to work. Please **use caution when you deviate** from this guide. Make sure you don't violate any assumptions GitLab makes about its environment. -If you find a bug/error in this guide please **submit a pull request** following the [`contributing guide`](../../CONTRIBUTING.md). +If you find a bug/error in this guide please **submit a pull request** following the [contributing guide](../../CONTRIBUTING.md). - - - @@ -69,6 +77,10 @@ does not ship with one. The recommended mail server is postfix and you can insta # 2. Ruby +Remove old 1.8 ruby if present + + sudo apt-get remove ruby1.8 + Download and compile it: mkdir /tmp/ruby && cd /tmp/ruby @@ -137,10 +149,10 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install cd /home/git/gitlab # Checkout to stable release - sudo -u git -H git checkout 5-1-stable + sudo -u git -H git checkout 5-2-stable **Note:** -You can change `5-1-stable` to `master` if you want the *bleeding edge* version, but +You can change `5-2-stable` to `master` if you want the *bleeding edge* version, but do so with caution! ## Configure it @@ -207,7 +219,7 @@ Make sure to update username/password in config/database.yml. sudo -u git -H bundle install --deployment --without development test mysql -## Initialise Database and Activate Advanced Features +## Initialize Database and Activate Advanced Features sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production @@ -216,7 +228,7 @@ Make sure to update username/password in config/database.yml. Download the init script (will be /etc/init.d/gitlab): - sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/master/lib/support/init.d/gitlab + sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab sudo chmod +x /etc/init.d/gitlab Make GitLab start on boot: @@ -257,7 +269,7 @@ If you can't or don't want to use Nginx as your web server, have a look at the Download an example site config: - sudo curl --output /etc/nginx/sites-available/gitlab https://raw.github.com/gitlabhq/gitlabhq/master/lib/support/nginx/gitlab + sudo cp lib/support/nginx/gitlab /etc/nginx/sites-available/gitlab sudo ln -s /etc/nginx/sites-available/gitlab /etc/nginx/sites-enabled/gitlab Make sure to edit the config file to match your setup: @@ -282,7 +294,7 @@ The setup has created an admin account for you. You can use it to log in: 5iveL!fe **Important Note:** -Please go over to your profile page and immediately chage the password, so +Please go over to your profile page and immediately change the password, so nobody can access your GitLab by using this login information later on. **Enjoy!** @@ -326,10 +338,10 @@ GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation. -* Add `gem "omniauth-your-auth-provider"` to the [Gemfile](https://github.com/gitlabhq/gitlabhq/blob/master/Gemfile#L18) +* Add `gem "omniauth-your-auth-provider"` to the [Gemfile](https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/Gemfile#L18) * Run `sudo -u git -H bundle install` to install the new gem(s) -* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://github.com/gitlabhq/gitlabhq/blob/master/config/gitlab.yml.example#L53) as a reference) -* Add icons for the new provider into the [vendor/assets/images/authbuttons](https://github.com/gitlabhq/gitlabhq/tree/master/vendor/assets/images/authbuttons) directory (you can find some more popular ones over at https://github.com/intridea/authbuttons) +* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/config/gitlab.yml.example#L53) as a reference) +* Add icons for the new provider into the [vendor/assets/images/authbuttons](https://github.com/gitlabhq/gitlabhq/tree/5-2-stable/vendor/assets/images/authbuttons) directory (you can find some more popular ones over at https://github.com/intridea/authbuttons) * Restart GitLab ### Examples diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md index 338e885851d..68c1a72b230 100644 --- a/doc/raketasks/maintenance.md +++ b/doc/raketasks/maintenance.md @@ -66,7 +66,7 @@ python2 is supported version? ... yes Checking Environment ... Finished -Checking Gitlab Shell ... +Checking GitLab Shell ... GitLab Shell version? ... OK (1.2.0) Repo base directory exists? ... yes @@ -76,7 +76,7 @@ Repo base access is drwxrws---? ... yes post-receive hook up-to-date? ... yes post-receive hooks in repos are links: ... yes -Checking Gitlab Shell ... Finished +Checking GitLab Shell ... Finished Checking Sidekiq ... @@ -116,6 +116,8 @@ bundle exec rake gitlab:satellites:create RAILS_ENV=production Notes: * project owner will be a first admin +* groups will be created as needed +* group owner will be the first admin * existing projects will be skipped How to use: @@ -132,5 +134,8 @@ Example output: ``` Processing abcd.git * Created abcd (abcd.git) +Processing group/xyz.git + * Created Group group (2) + * Created xyz (group/xyz.git) [...] ``` diff --git a/features/project/service.feature b/features/project/service.feature index ca8a4756056..6bb0c3e2c57 100644 --- a/features/project/service.feature +++ b/features/project/service.feature @@ -12,3 +12,9 @@ Feature: Project Services And I click gitlab-ci service link And I fill gitlab-ci settings Then I should see service settings saved + + Scenario: Activate hipchat service + When I visit project "Shop" services page + And I click hipchat service link + And I fill hipchat settings + Then I should see hipchat service settings saved diff --git a/features/project/source/search_code.feature b/features/project/source/search_code.feature index f62adb5d056..13f15cc922f 100644 --- a/features/project/source/search_code.feature +++ b/features/project/source/search_code.feature @@ -4,6 +4,6 @@ Feature: Project Search code And I own project "Shop" Given I visit project source page - Scenario: Search for term "Welcome to Gitlab" - When I search for term "Welcome to Gitlab" - Then I should see files from repository containing "Welcome to Gitlab" + Scenario: Search for term "Welcome to GitLab" + When I search for term "Welcome to GitLab" + Then I should see files from repository containing "Welcome to GitLab" diff --git a/features/steps/project/project_search_code.rb b/features/steps/project/project_search_code.rb index 0dcd17a0d13..d117b019a15 100644 --- a/features/steps/project/project_search_code.rb +++ b/features/steps/project/project_search_code.rb @@ -3,14 +3,14 @@ class ProjectSearchCode < Spinach::FeatureSteps include SharedProject include SharedPaths - When 'I search for term "Welcome to Gitlab"' do - fill_in "search", with: "Welcome to Gitlab" + When 'I search for term "Welcome to GitLab"' do + fill_in "search", with: "Welcome to GitLab" click_button "Go" click_link 'Repository Code' end - Then 'I should see files from repository containing "Welcome to Gitlab"' do - page.should have_content "Welcome to Gitlab" + Then 'I should see files from repository containing "Welcome to GitLab"' do + page.should have_content "Welcome to GitLab" page.should have_content "GitLab is a free project and repository management application" end diff --git a/features/steps/project/project_services.rb b/features/steps/project/project_services.rb index b1668ff7207..10feb8ea8be 100644 --- a/features/steps/project/project_services.rb +++ b/features/steps/project/project_services.rb @@ -9,7 +9,8 @@ class ProjectServices < Spinach::FeatureSteps Then 'I should see list of available services' do page.should have_content 'Services' - page.should have_content 'Jenkins' + page.should have_content 'Campfire' + page.should have_content 'Hipchat' page.should have_content 'GitLab CI' end @@ -19,12 +20,28 @@ class ProjectServices < Spinach::FeatureSteps And 'I fill gitlab-ci settings' do check 'Active' - fill_in 'Project URL', with: 'http://ci.gitlab.org/projects/3' - fill_in 'CI Project token', with: 'verySecret' + fill_in 'Project url', with: 'http://ci.gitlab.org/projects/3' + fill_in 'Token', with: 'verySecret' click_button 'Save' end Then 'I should see service settings saved' do - find_field('Project URL').value.should == 'http://ci.gitlab.org/projects/3' + find_field('Project url').value.should == 'http://ci.gitlab.org/projects/3' end + + And 'I click hipchat service link' do + click_link 'Hipchat' + end + + And 'I fill hipchat settings' do + check 'Active' + fill_in 'Room', with: 'gitlab' + fill_in 'Token', with: 'verySecret' + click_button 'Save' + end + + Then 'I should see hipchat service settings saved' do + find_field('Room').value.should == 'gitlab' + end + end diff --git a/lib/api/api.rb b/lib/api/api.rb index 28e6add73ed..c2752406122 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -27,6 +27,7 @@ module API mount Groups mount Users mount Projects + mount Repositories mount Issues mount Milestones mount Session diff --git a/lib/api/projects.rb b/lib/api/projects.rb index d9743b4539a..ddc403c12db 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -286,95 +286,6 @@ module API end end - # Get a project repository branches - # - # Parameters: - # id (required) - The ID of a project - # Example Request: - # GET /projects/:id/repository/branches - get ":id/repository/branches" do - present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject, project: user_project - end - - # Get a single branch - # - # Parameters: - # id (required) - The ID of a project - # branch (required) - The name of the branch - # Example Request: - # GET /projects/:id/repository/branches/:branch - get ":id/repository/branches/:branch" do - @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } - not_found!("Branch does not exist") if @branch.nil? - present @branch, with: Entities::RepoObject, project: user_project - end - - # Protect a single branch - # - # Parameters: - # id (required) - The ID of a project - # branch (required) - The name of the branch - # Example Request: - # PUT /projects/:id/repository/branches/:branch/protect - put ":id/repository/branches/:branch/protect" do - @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } - not_found! unless @branch - protected = user_project.protected_branches.find_by_name(@branch.name) - - unless protected - user_project.protected_branches.create(name: @branch.name) - end - - present @branch, with: Entities::RepoObject, project: user_project - end - - # Unprotect a single branch - # - # Parameters: - # id (required) - The ID of a project - # branch (required) - The name of the branch - # Example Request: - # PUT /projects/:id/repository/branches/:branch/unprotect - put ":id/repository/branches/:branch/unprotect" do - @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } - not_found! unless @branch - protected = user_project.protected_branches.find_by_name(@branch.name) - - if protected - protected.destroy - end - - present @branch, with: Entities::RepoObject, project: user_project - end - - # Get a project repository tags - # - # Parameters: - # id (required) - The ID of a project - # Example Request: - # GET /projects/:id/repository/tags - get ":id/repository/tags" do - present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject - end - - # Get a project repository commits - # - # Parameters: - # id (required) - The ID of a project - # ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used - # Example Request: - # GET /projects/:id/repository/commits - get ":id/repository/commits" do - authorize! :download_code, user_project - - page = params[:page] || 0 - 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) - present commits, with: Entities::RepoCommit - end - # Get a project snippets # # Parameters: @@ -479,32 +390,6 @@ module API present @snippet.content end - # Get a raw file contents - # - # Parameters: - # id (required) - The ID of a project - # sha (required) - The commit or branch name - # filepath (required) - The path to the file to display - # Example Request: - # GET /projects/:id/repository/commits/:sha/blob - get ":id/repository/commits/:sha/blob" do - authorize! :download_code, user_project - required_attributes! [:filepath] - - ref = params[:sha] - - repo = user_project.repository - - commit = repo.commit(ref) - not_found! "Commit" unless commit - - blob = Gitlab::Git::Blob.new(repo, commit.id, ref, params[:filepath]) - not_found! "File" unless blob.exists? - - content_type blob.mime_type - present blob.data - end - # Get a specific project's keys # # Example Request: diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb new file mode 100644 index 00000000000..964b9eb38ac --- /dev/null +++ b/lib/api/repositories.rb @@ -0,0 +1,133 @@ +module API + # Projects API + class Repositories < Grape::API + before { authenticate! } + + resource :projects do + helpers do + def handle_project_member_errors(errors) + if errors[:project_access].any? + error!(errors[:project_access], 422) + end + not_found! + end + end + + # Get a project repository branches + # + # Parameters: + # id (required) - The ID of a project + # Example Request: + # GET /projects/:id/repository/branches + get ":id/repository/branches" do + present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject, project: user_project + end + + # Get a single branch + # + # Parameters: + # id (required) - The ID of a project + # branch (required) - The name of the branch + # Example Request: + # GET /projects/:id/repository/branches/:branch + get ":id/repository/branches/:branch" do + @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } + not_found!("Branch does not exist") if @branch.nil? + present @branch, with: Entities::RepoObject, project: user_project + end + + # Protect a single branch + # + # Parameters: + # id (required) - The ID of a project + # branch (required) - The name of the branch + # Example Request: + # PUT /projects/:id/repository/branches/:branch/protect + put ":id/repository/branches/:branch/protect" do + @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } + not_found! unless @branch + protected = user_project.protected_branches.find_by_name(@branch.name) + + unless protected + user_project.protected_branches.create(name: @branch.name) + end + + present @branch, with: Entities::RepoObject, project: user_project + end + + # Unprotect a single branch + # + # Parameters: + # id (required) - The ID of a project + # branch (required) - The name of the branch + # Example Request: + # PUT /projects/:id/repository/branches/:branch/unprotect + put ":id/repository/branches/:branch/unprotect" do + @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } + not_found! unless @branch + protected = user_project.protected_branches.find_by_name(@branch.name) + + if protected + protected.destroy + end + + present @branch, with: Entities::RepoObject, project: user_project + end + + # Get a project repository tags + # + # Parameters: + # id (required) - The ID of a project + # Example Request: + # GET /projects/:id/repository/tags + get ":id/repository/tags" do + present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject + end + + # Get a project repository commits + # + # Parameters: + # id (required) - The ID of a project + # ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used + # Example Request: + # GET /projects/:id/repository/commits + get ":id/repository/commits" do + authorize! :download_code, user_project + + page = params[:page] || 0 + 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) + present commits, with: Entities::RepoCommit + end + + # Get a raw file contents + # + # Parameters: + # id (required) - The ID of a project + # sha (required) - The commit or branch name + # filepath (required) - The path to the file to display + # Example Request: + # GET /projects/:id/repository/commits/:sha/blob + get ":id/repository/commits/:sha/blob" do + authorize! :download_code, user_project + required_attributes! [:filepath] + + ref = params[:sha] + + repo = user_project.repository + + commit = repo.commit(ref) + not_found! "Commit" unless commit + + blob = Gitlab::Git::Blob.new(repo, commit.id, ref, params[:filepath]) + not_found! "File" unless blob.exists? + + content_type blob.mime_type + present blob.data + end + end + end +end + diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index 78d2196fbbe..ff1d1b13cc9 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -70,5 +70,24 @@ module Gitlab def log Gitlab::AppLogger end + + def ldap_auth(login, password) + # Check user against LDAP backend if user is not authenticated + # Only check with valid login and password to prevent anonymous bind results + return nil unless ldap_conf.enabled && !login.blank? && !password.blank? + + ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf) + ldap_user = ldap.bind_as( + filter: Net::LDAP::Filter.eq(ldap.uid, login), + size: 1, + password: password + ) + + User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user + end + + def ldap_conf + @ldap_conf ||= Gitlab.config.ldap + end end end diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index 6a411aabcc6..4f3f7b02a5b 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -32,20 +32,11 @@ module Grack if @auth.provided? # Authentication with username and password login, password = @auth.credentials - self.user = User.find_by_email(login) || User.find_by_username(login) - - # If the provided login was not a known email or username - # then user is nil - if user.nil? - # Second chance - try LDAP authentication - return false unless Gitlab.config.ldap.enabled - ldap_auth(login,password) - return false unless !user.nil? - else - return false unless user.valid_password?(password) - end - - Gitlab::ShellEnv.set_env(user) + + @user = authenticate(login, password) + return false unless @user + + Gitlab::ShellEnv.set_env(@user) end # Git upload and receive @@ -58,21 +49,35 @@ module Grack end end + def authenticate(login, password) + user = User.find_by_email(login) || User.find_by_username(login) + + # If the provided login was not a known email or username + # then user is nil + if user.nil? || user.ldap_user? + # Second chance - try LDAP authentication + return nil unless ldap_conf.enabled + + auth = Gitlab::Auth.new + auth.ldap_auth(login, password) + else + return user if user.valid_password?(password) + end + end + def ldap_auth(login, password) # Check user against LDAP backend if user is not authenticated # Only check with valid login and password to prevent anonymous bind results - gl = Gitlab.config - if gl.ldap.enabled && !login.blank? && !password.blank? - ldap = OmniAuth::LDAP::Adaptor.new(gl.ldap) - ldap_user = ldap.bind_as( - filter: Net::LDAP::Filter.eq(ldap.uid, login), - size: 1, - password: password - ) - if ldap_user - self.user = User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') - end - end + return nil unless ldap_conf.enabled && !login.blank? && !password.blank? + + ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf) + ldap_user = ldap.bind_as( + filter: Net::LDAP::Filter.eq(ldap.uid, login), + size: 1, + password: password + ) + + User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user end def validate_get_request @@ -139,5 +144,9 @@ module Grack abilities end end + + def ldap_conf + @ldap_conf ||= Gitlab.config.ldap + end end# Auth end# Grack diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index 47996594e3b..4c5499bbf23 100644 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/gitlab @@ -24,7 +24,7 @@ SIDEKIQ_PID="$PID_PATH/sidekiq.pid" STOP_SIDEKIQ="RAILS_ENV=production bundle exec rake sidekiq:stop" START_SIDEKIQ="RAILS_ENV=production bundle exec rake sidekiq:start" NAME="gitlab" -DESC="Gitlab service" +DESC="GitLab service" check_pid(){ if [ -f $WEB_SERVER_PID ]; then diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 77f5c02be2e..e7cfa4424ab 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -362,10 +362,10 @@ namespace :gitlab do namespace :gitlab_shell do - desc "GITLAB | Check the configuration of Gitlab Shell" + desc "GITLAB | Check the configuration of GitLab Shell" task check: :environment do warn_user_is_not_gitlab - start_checking "Gitlab Shell" + start_checking "GitLab Shell" check_gitlab_shell check_repo_base_exists @@ -375,7 +375,7 @@ namespace :gitlab do check_post_receive_hook_is_up_to_date check_repos_post_receive_hooks_is_link - finished_checking "Gitlab Shell" + finished_checking "GitLab Shell" end @@ -410,12 +410,12 @@ namespace :gitlab do puts "no".red puts "#{repo_base_path} is missing".red try_fixing_it( - "This should have been created when setting up Gitlab Shell.", + "This should have been created when setting up GitLab Shell.", "Make sure it's set correctly in config/gitlab.yml", - "Make sure Gitlab Shell is installed correctly." + "Make sure GitLab Shell is installed correctly." ) for_more_information( - see_installation_guide_section "Gitlab Shell" + see_installation_guide_section "GitLab Shell" ) fix_and_rerun end @@ -460,7 +460,7 @@ namespace :gitlab do "find #{repo_base_path} -type d -print0 | sudo xargs -0 chmod g+s" ) for_more_information( - see_installation_guide_section "Gitlab Shell" + see_installation_guide_section "GitLab Shell" ) fix_and_rerun end @@ -486,7 +486,7 @@ namespace :gitlab do "sudo chown -R #{gitlab_shell_ssh_user}:#{gitlab_shell_owner_group} #{repo_base_path}" ) for_more_information( - see_installation_guide_section "Gitlab Shell" + see_installation_guide_section "GitLab Shell" ) fix_and_rerun end @@ -535,7 +535,7 @@ namespace :gitlab do File.realpath(project_hook_file) == File.realpath(gitlab_shell_hook_file) puts "ok".green else - puts "not a link to Gitlab Shell's hook".red + puts "not a link to GitLab Shell's hook".red try_fixing_it( "sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}" ) @@ -588,7 +588,7 @@ namespace :gitlab do def check_sidekiq_running print "Running? ... " - if run_and_match("ps aux | grep -i sidekiq", /sidekiq \d\.\d\.\d.+$/) + if run_and_match("ps aux | grep -i sidekiq", /sidekiq \d+\.\d+\.\d+.+$/) puts "yes".green else puts "no".red diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake index acf6abedd19..c11284e3d78 100644 --- a/lib/tasks/gitlab/import.rake +++ b/lib/tasks/gitlab/import.rake @@ -13,42 +13,61 @@ namespace :gitlab do task repos: :environment do git_base_path = Gitlab.config.gitlab_shell.repos_path - repos_to_import = Dir.glob(git_base_path + '/*') + repos_to_import = Dir.glob(git_base_path + '/**/*.git') namespaces = Namespace.pluck(:path) repos_to_import.each do |repo_path| - repo_name = File.basename repo_path + # strip repo base path + repo_path[0..git_base_path.length] = '' - # Skip if group or user - next if namespaces.include?(repo_name) + path = repo_path.sub(/\.git$/, '') + name = File.basename path + group_name = File.dirname path + group_name = nil if group_name == '.' - # skip if not git repo - next unless repo_name =~ /.git$/ + # Skip if group or user + next if namespaces.include?(name) - next if repo_name == 'gitolite-admin.git' + next if name == 'gitolite-admin' - path = repo_name.sub(/\.git$/, '') + puts "Processing #{repo_path}".yellow project = Project.find_with_namespace(path) - puts "Processing #{repo_name}".yellow - if project - puts " * #{project.name} (#{repo_name}) exists" + puts " * #{project.name} (#{repo_path}) exists" else user = User.admins.first project_params = { - name: path, + name: name, } + # find group namespace + if group_name + group = Group.find_by_path(group_name) + # create group namespace + if !group + group = Group.new(:name => group_name) + group.path = group_name + group.owner = user + if group.save + puts " * Created Group #{group.name} (#{group.id})".green + else + puts " * Failed trying to create group #{group.name}".red + end + end + # set project group + project_params[:namespace_id] = group.id + end + project = Projects::CreateContext.new(user, project_params).execute if project.valid? - puts " * Created #{project.name} (#{repo_name})".green + puts " * Created #{project.name} (#{repo_path})".green else - puts " * Failed trying to create #{project.name} (#{repo_name})".red + puts " * Failed trying to create #{project.name} (#{repo_path})".red end end end diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb index aa72bcee005..53d31766a00 100644 --- a/spec/features/gitlab_flavored_markdown_spec.rb +++ b/spec/features/gitlab_flavored_markdown_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Gitlab Flavored Markdown" do +describe "GitLab Flavored Markdown" do let(:project) { create(:project_with_code) } let(:issue) { create(:issue, project: project) } let(:merge_request) { create(:merge_request, project: project) } diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index 4140ba484f0..23b18fbf0eb 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -370,7 +370,7 @@ describe GitlabMarkdownHelper do @wiki.stub(:content).and_return('wiki content') end - it "should use Gitlab Flavored Markdown for markdown files" do + it "should use GitLab Flavored Markdown for markdown files" do @wiki.stub(:format).and_return(:markdown) helper.should_receive(:markdown).with('wiki content') diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 6b7799f7af7..04b4ce1763e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -61,10 +61,6 @@ describe Project do it { should ensure_length_of(:path).is_within(0..255) } it { should ensure_length_of(:description).is_within(0..2000) } it { should validate_presence_of(:creator) } - it { should ensure_inclusion_of(:issues_enabled).in_array([true, false]) } - 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 diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 4346bfe8f2e..de0631d5b70 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -173,75 +173,6 @@ describe API::API do end end - describe "GET /projects/:id/repository/branches" do - it "should return an array of project branches" do - get api("/projects/#{project.id}/repository/branches", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['name'].should == project.repo.heads.sort_by(&:name).first.name - end - end - - describe "GET /projects/:id/repository/branches/:branch" do - it "should return the branch information for a single branch" do - get api("/projects/#{project.id}/repository/branches/new_design", user) - response.status.should == 200 - - json_response['name'].should == 'new_design' - json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' - json_response['protected'].should == false - end - - it "should return a 404 error if branch is not available" do - get api("/projects/#{project.id}/repository/branches/unknown", user) - response.status.should == 404 - end - end - - describe "PUT /projects/:id/repository/branches/:branch/protect" do - it "should protect a single branch" do - put api("/projects/#{project.id}/repository/branches/new_design/protect", user) - response.status.should == 200 - - json_response['name'].should == 'new_design' - json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' - json_response['protected'].should == true - end - - it "should return a 404 error if branch not found" do - put api("/projects/#{project.id}/repository/branches/unknown/protect", user) - response.status.should == 404 - end - - it "should return success when protect branch again" do - put api("/projects/#{project.id}/repository/branches/new_design/protect", user) - put api("/projects/#{project.id}/repository/branches/new_design/protect", user) - response.status.should == 200 - end - end - - describe "PUT /projects/:id/repository/branches/:branch/unprotect" do - it "should unprotect a single branch" do - put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) - response.status.should == 200 - - json_response['name'].should == 'new_design' - json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' - json_response['protected'].should == false - end - - it "should return success when unprotect branch" do - put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user) - response.status.should == 404 - end - - it "should return success when unprotect branch again" do - put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) - put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) - response.status.should == 200 - end - end - describe "GET /projects/:id/members" do it "should return project team members" do get api("/projects/#{project.id}/members", user) @@ -491,35 +422,6 @@ describe API::API do end end - describe "GET /projects/:id/repository/tags" do - it "should return an array of project tags" do - get api("/projects/#{project.id}/repository/tags", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['name'].should == project.repo.tags.sort_by(&:name).reverse.first.name - end - end - - describe "GET /projects/:id/repository/commits" do - context "authorized user" do - before { project.team << [user2, :reporter] } - - it "should return project commits" do - get api("/projects/#{project.id}/repository/commits", user) - response.status.should == 200 - - json_response.should be_an Array - json_response.first['id'].should == project.repository.commit.id - end - end - - context "unauthorized user" do - it "should not return project commits" do - get api("/projects/#{project.id}/repository/commits") - response.status.should == 401 - end - end - end describe "GET /projects/:id/snippets" do it "should return an array of project snippets" do @@ -613,28 +515,6 @@ describe API::API do end end - describe "GET /projects/:id/repository/commits/:sha/blob" do - it "should get the raw file contents" do - get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user) - response.status.should == 200 - end - - it "should return 404 for invalid branch_name" do - get api("/projects/#{project.id}/repository/commits/invalid_branch_name/blob?filepath=README.md", user) - response.status.should == 404 - end - - it "should return 404 for invalid file" do - get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.invalid", user) - response.status.should == 404 - end - - it "should return a 400 error if filepath is missing" do - get api("/projects/#{project.id}/repository/commits/master/blob", user) - response.status.should == 400 - end - end - describe :deploy_keys do let(:deploy_keys_project) { create(:deploy_keys_project, project: project) } let(:deploy_key) { deploy_keys_project.deploy_key } diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb new file mode 100644 index 00000000000..31176316af2 --- /dev/null +++ b/spec/requests/api/repositories_spec.rb @@ -0,0 +1,135 @@ +require 'spec_helper' + +describe API::API do + include ApiHelpers + before(:each) { enable_observers } + + let(:user) { create(:user) } + let(:user2) { create(:user) } + let!(:project) { create(:project_with_code, creator_id: user.id) } + let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } + + before { project.team << [user, :reporter] } + + + describe "GET /projects/:id/repository/branches" do + it "should return an array of project branches" do + get api("/projects/#{project.id}/repository/branches", user) + response.status.should == 200 + json_response.should be_an Array + json_response.first['name'].should == project.repo.heads.sort_by(&:name).first.name + end + end + + describe "GET /projects/:id/repository/branches/:branch" do + it "should return the branch information for a single branch" do + get api("/projects/#{project.id}/repository/branches/new_design", user) + response.status.should == 200 + + json_response['name'].should == 'new_design' + json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' + json_response['protected'].should == false + end + + it "should return a 404 error if branch is not available" do + get api("/projects/#{project.id}/repository/branches/unknown", user) + response.status.should == 404 + end + end + + describe "PUT /projects/:id/repository/branches/:branch/protect" do + it "should protect a single branch" do + put api("/projects/#{project.id}/repository/branches/new_design/protect", user) + response.status.should == 200 + + json_response['name'].should == 'new_design' + json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' + json_response['protected'].should == true + end + + it "should return a 404 error if branch not found" do + put api("/projects/#{project.id}/repository/branches/unknown/protect", user) + response.status.should == 404 + end + + it "should return success when protect branch again" do + put api("/projects/#{project.id}/repository/branches/new_design/protect", user) + put api("/projects/#{project.id}/repository/branches/new_design/protect", user) + response.status.should == 200 + end + end + + describe "PUT /projects/:id/repository/branches/:branch/unprotect" do + it "should unprotect a single branch" do + put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) + response.status.should == 200 + + json_response['name'].should == 'new_design' + json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' + json_response['protected'].should == false + end + + it "should return success when unprotect branch" do + put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user) + response.status.should == 404 + end + + it "should return success when unprotect branch again" do + put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) + put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) + response.status.should == 200 + end + end + + describe "GET /projects/:id/repository/tags" do + it "should return an array of project tags" do + get api("/projects/#{project.id}/repository/tags", user) + response.status.should == 200 + json_response.should be_an Array + json_response.first['name'].should == project.repo.tags.sort_by(&:name).reverse.first.name + end + end + + describe "GET /projects/:id/repository/commits" do + context "authorized user" do + before { project.team << [user2, :reporter] } + + it "should return project commits" do + get api("/projects/#{project.id}/repository/commits", user) + response.status.should == 200 + + json_response.should be_an Array + json_response.first['id'].should == project.repository.commit.id + end + end + + context "unauthorized user" do + it "should not return project commits" do + get api("/projects/#{project.id}/repository/commits") + response.status.should == 401 + end + end + end + + describe "GET /projects/:id/repository/commits/:sha/blob" do + it "should get the raw file contents" do + get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user) + response.status.should == 200 + end + + it "should return 404 for invalid branch_name" do + get api("/projects/#{project.id}/repository/commits/invalid_branch_name/blob?filepath=README.md", user) + response.status.should == 404 + end + + it "should return 404 for invalid file" do + get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.invalid", user) + response.status.should == 404 + end + + it "should return a 400 error if filepath is missing" do + get api("/projects/#{project.id}/repository/commits/master/blob", user) + response.status.should == 400 + end + end +end |