Commit 3374027e3a4e4eb040e59294a9ced9d7886a71e2
Exists in
master
and in
4 other branches
Merge branch 'master' into fixes/api, code clean up and tests fixed
Conflicts: doc/api/projects.md spec/requests/api/projects_spec.rb
Showing
49 changed files
with
819 additions
and
162 deletions
Show diff stats
Gemfile
| 1 | -source "http://rubygems.org" | |
| 1 | +source "https://rubygems.org" | |
| 2 | 2 | |
| 3 | 3 | def darwin_only(require_as) |
| 4 | 4 | RUBY_PLATFORM.include?('darwin') && require_as |
| ... | ... | @@ -103,6 +103,9 @@ gem 'settingslogic' |
| 103 | 103 | gem "foreman" |
| 104 | 104 | gem "git" |
| 105 | 105 | |
| 106 | +# Cache | |
| 107 | +gem "redis-rails" | |
| 108 | + | |
| 106 | 109 | group :assets do |
| 107 | 110 | gem "sass-rails", "~> 3.2.5" |
| 108 | 111 | gem "coffee-rails", "~> 3.2.2" | ... | ... |
Gemfile.lock
| ... | ... | @@ -13,7 +13,7 @@ GIT |
| 13 | 13 | raphael-rails (2.1.0) |
| 14 | 14 | |
| 15 | 15 | GEM |
| 16 | - remote: http://rubygems.org/ | |
| 16 | + remote: https://rubygems.org/ | |
| 17 | 17 | specs: |
| 18 | 18 | actionmailer (3.2.12) |
| 19 | 19 | actionpack (= 3.2.12) |
| ... | ... | @@ -329,8 +329,24 @@ GEM |
| 329 | 329 | json (~> 1.4) |
| 330 | 330 | redcarpet (2.2.2) |
| 331 | 331 | redis (3.0.2) |
| 332 | + redis-actionpack (3.2.3) | |
| 333 | + actionpack (~> 3.2.3) | |
| 334 | + redis-rack (~> 1.4.0) | |
| 335 | + redis-store (~> 1.1.0) | |
| 336 | + redis-activesupport (3.2.3) | |
| 337 | + activesupport (~> 3.2.3) | |
| 338 | + redis-store (~> 1.1.0) | |
| 332 | 339 | redis-namespace (1.2.1) |
| 333 | 340 | redis (~> 3.0.0) |
| 341 | + redis-rack (1.4.2) | |
| 342 | + rack (~> 1.4.1) | |
| 343 | + redis-store (~> 1.1.0) | |
| 344 | + redis-rails (3.2.3) | |
| 345 | + redis-actionpack (~> 3.2.3) | |
| 346 | + redis-activesupport (~> 3.2.3) | |
| 347 | + redis-store (~> 1.1.0) | |
| 348 | + redis-store (1.1.3) | |
| 349 | + redis (>= 2.2.0) | |
| 334 | 350 | request_store (1.0.5) |
| 335 | 351 | rspec (2.12.0) |
| 336 | 352 | rspec-core (~> 2.12.0) |
| ... | ... | @@ -504,6 +520,7 @@ DEPENDENCIES |
| 504 | 520 | rb-fsevent |
| 505 | 521 | rb-inotify |
| 506 | 522 | redcarpet (~> 2.2.2) |
| 523 | + redis-rails | |
| 507 | 524 | rspec-rails (= 2.12.2) |
| 508 | 525 | sass-rails (~> 3.2.5) |
| 509 | 526 | sdoc | ... | ... |
README.md
| ... | ... | @@ -5,14 +5,14 @@ |
| 5 | 5 | ### GitLab allows you to |
| 6 | 6 | * keep your code secure on your own server |
| 7 | 7 | * manage repositories, users and access permissions |
| 8 | - * communicate though issues, line-comments and wiki's | |
| 9 | - * perform code reviews with merge requests | |
| 8 | + * communicate through issues, line-comments and wiki pages | |
| 9 | + * perform code review with merge requests | |
| 10 | 10 | |
| 11 | 11 | ### GitLab is |
| 12 | 12 | |
| 13 | 13 | * powered by Ruby on Rails |
| 14 | 14 | * completely free and open source (MIT license) |
| 15 | -* used by 10.000 organization to keep their code secure | |
| 15 | +* used by 10.000 organizations to keep their code secure | |
| 16 | 16 | |
| 17 | 17 | ### Code status |
| 18 | 18 | |
| ... | ... | @@ -34,28 +34,35 @@ |
| 34 | 34 | |
| 35 | 35 | ### Requirements |
| 36 | 36 | |
| 37 | -* Ubuntu/Debian* | |
| 37 | +* Ubuntu/Debian** | |
| 38 | 38 | * ruby 1.9.3+ |
| 39 | 39 | * MySQL |
| 40 | 40 | * git |
| 41 | 41 | * gitlab-shell |
| 42 | 42 | * redis |
| 43 | 43 | |
| 44 | -* More details are in the [requirements doc](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/requirements.md) | |
| 44 | +** More details are in the [requirements doc](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/requirements.md) | |
| 45 | 45 | |
| 46 | 46 | ### Installation |
| 47 | 47 | |
| 48 | -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. | |
| 48 | +#### For production | |
| 49 | 49 | |
| 50 | -* [Installation guide for latest stable release](https://github.com/gitlabhq/gitlabhq/blob/4-2-stable/doc/install/installation.md) | |
| 50 | +Follow the installation guide for production server. | |
| 51 | 51 | |
| 52 | -* [Installation guide for the current master branch](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md) | |
| 52 | +* [Installation guide for latest stable release (4.2)](https://github.com/gitlabhq/gitlabhq/blob/4-2-stable/doc/install/installation.md) - **Recommended** | |
| 53 | + | |
| 54 | +* [Installation guide for the current master branch (5.0)](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md) | |
| 55 | + | |
| 56 | + | |
| 57 | +#### For development | |
| 58 | + | |
| 59 | +If you want to contribute, please first read our [Contributing Guidelines](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) and then we suggest you to use the Vagrant virtual machine project to get an environment working sandboxed and with all dependencies. | |
| 53 | 60 | |
| 54 | 61 | * [Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) |
| 55 | 62 | |
| 56 | 63 | ### Starting |
| 57 | 64 | |
| 58 | -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: | |
| 65 | +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 | |
| 59 | 66 | |
| 60 | 67 | sudo service gitlab start |
| 61 | 68 | |
| ... | ... | @@ -63,18 +70,18 @@ You can either follow the "ordinary" Installation guide to install it on a machi |
| 63 | 70 | |
| 64 | 71 | sudo /etc/init.d/gitlab restart |
| 65 | 72 | |
| 66 | -2. Start it with [Foreman](https://github.com/ddollar/foreman) in development model | |
| 73 | +2. Start it with [Foreman](https://github.com/ddollar/foreman) in development mode | |
| 67 | 74 | |
| 68 | 75 | bundle exec foreman start -p 3000 |
| 69 | 76 | |
| 70 | -3. Start it manually in development mode | |
| 77 | + or start it manually | |
| 71 | 78 | |
| 72 | 79 | bundle exec rails s |
| 73 | 80 | bundle exec rake sidekiq:start |
| 74 | 81 | |
| 75 | 82 | ### Running the tests |
| 76 | 83 | |
| 77 | -* Seed the database with | |
| 84 | +* Seed the database | |
| 78 | 85 | |
| 79 | 86 | bundle exec rake db:setup RAILS_ENV=test |
| 80 | 87 | bundle exec rake db:seed_fu RAILS_ENV=test | ... | ... |
app/assets/javascripts/tree.js.coffee
| ... | ... | @@ -11,12 +11,7 @@ $ -> |
| 11 | 11 | # Make the entire tree-item row clickable, but not if clicking another link (like a commit message) |
| 12 | 12 | $("#tree-slider .tree-item").live 'click', (e) -> |
| 13 | 13 | $('.tree-item-file-name a', this).trigger('click') if (e.target.nodeName != "A") |
| 14 | - | |
| 15 | - # Show/Hide the loading spinner | |
| 16 | - $('#tree-slider .tree-item-file-name a, .breadcrumb a, .project-refs-form').live | |
| 17 | - "ajax:beforeSend": -> $('.tree_progress').addClass("loading") | |
| 18 | - "ajax:complete": -> $('.tree_progress').removeClass("loading") | |
| 19 | - | |
| 14 | + | |
| 20 | 15 | # Maintain forward/back history while browsing the file tree |
| 21 | 16 | ((window) -> |
| 22 | 17 | History = window.History |
| ... | ... | @@ -33,7 +28,12 @@ $ -> |
| 33 | 28 | |
| 34 | 29 | History.Adapter.bind window, 'statechange', -> |
| 35 | 30 | state = History.getState() |
| 36 | - window.ajaxGet(state.url) | |
| 31 | + $.ajax({ | |
| 32 | + url: state.url, | |
| 33 | + dataType: 'script', | |
| 34 | + beforeSend: -> $('.tree_progress').addClass("loading"), | |
| 35 | + complete: -> $('.tree_progress').removeClass("loading") | |
| 36 | + }) | |
| 37 | 37 | )(window) |
| 38 | 38 | |
| 39 | 39 | # See if there are lines selected | ... | ... |
app/assets/stylesheets/gitlab_bootstrap/blocks.scss
app/assets/stylesheets/gitlab_bootstrap/common.scss
app/assets/stylesheets/sections/projects.scss
| ... | ... | @@ -120,3 +120,16 @@ ul.nav.nav-projects-tabs { |
| 120 | 120 | .team_member_row form { |
| 121 | 121 | margin: 0px; |
| 122 | 122 | } |
| 123 | + | |
| 124 | +.public-projects { | |
| 125 | + li { | |
| 126 | + margin-top: 8px; | |
| 127 | + margin-bottom: 5px; | |
| 128 | + border-bottom: 1px solid #eee; | |
| 129 | + | |
| 130 | + .description { | |
| 131 | + margin-left: 22px; | |
| 132 | + color: #aaa; | |
| 133 | + } | |
| 134 | + } | |
| 135 | +} | ... | ... |
app/models/merge_request.rb
| ... | ... | @@ -91,7 +91,7 @@ class MergeRequest < ActiveRecord::Base |
| 91 | 91 | |
| 92 | 92 | def validate_branches |
| 93 | 93 | if target_branch == source_branch |
| 94 | - errors.add :base, "You can not use same branch for source and target branches" | |
| 94 | + errors.add :branch_conflict, "You can not use same branch for source and target branches" | |
| 95 | 95 | end |
| 96 | 96 | end |
| 97 | 97 | ... | ... |
app/models/repository.rb
| 1 | 1 | class Repository |
| 2 | + include Gitlab::Popen | |
| 3 | + | |
| 2 | 4 | # Repository directory name with namespace direcotry |
| 3 | 5 | # Examples: |
| 4 | 6 | # gitlab/gitolite |
| ... | ... | @@ -147,4 +149,21 @@ class Repository |
| 147 | 149 | |
| 148 | 150 | file_path |
| 149 | 151 | end |
| 152 | + | |
| 153 | + # Return repo size in megabytes | |
| 154 | + # Cached in redis | |
| 155 | + def size | |
| 156 | + Rails.cache.fetch(cache_key(:size)) do | |
| 157 | + size = popen('du -s', path_to_repo).first.strip.to_i | |
| 158 | + (size.to_f / 1024).round(2) | |
| 159 | + end | |
| 160 | + end | |
| 161 | + | |
| 162 | + def expire_cache | |
| 163 | + Rails.cache.delete(cache_key(:size)) | |
| 164 | + end | |
| 165 | + | |
| 166 | + def cache_key(type) | |
| 167 | + "#{type}:#{path_with_namespace}" | |
| 168 | + end | |
| 150 | 169 | end | ... | ... |
app/services/git_push_service.rb
app/views/devise/sessions/_new_ldap.html.haml
| 1 | 1 | = form_tag(user_omniauth_callback_path(:ldap), :class => "login-box", :id => 'new_ldap_user' ) do |
| 2 | 2 | = image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo" |
| 3 | - = text_field_tag :username, nil, {:class => "text top", :placeholder => "LDAP Login"} | |
| 3 | + = text_field_tag :username, nil, {:class => "text top", :placeholder => "LDAP Login", :autofocus => "autofocus"} | |
| 4 | 4 | = password_field_tag :password, nil, {:class => "text bottom", :placeholder => "Password"} |
| 5 | 5 | %br/ |
| 6 | 6 | = submit_tag "LDAP Sign in", :class => "btn-primary btn" | ... | ... |
app/views/events/_event.html.haml
| 1 | 1 | - if event.proper? |
| 2 | - %div.event-item | |
| 3 | - %span.cgray.pull-right | |
| 4 | - #{time_ago_in_words(event.created_at)} ago. | |
| 2 | + = cache event do | |
| 3 | + %div.event-item | |
| 4 | + %span.cgray.pull-right | |
| 5 | + #{time_ago_in_words(event.created_at)} ago. | |
| 5 | 6 | |
| 6 | - = image_tag gravatar_icon(event.author_email), class: "avatar s24" | |
| 7 | + = image_tag gravatar_icon(event.author_email), class: "avatar s24" | |
| 7 | 8 | |
| 8 | - - if event.push? | |
| 9 | - = render "events/event/push", event: event | |
| 10 | - .clearfix | |
| 11 | - - elsif event.note? | |
| 12 | - = render "events/event/note", event: event | |
| 13 | - - else | |
| 14 | - = render "events/event/common", event: event | |
| 9 | + - if event.push? | |
| 10 | + = render "events/event/push", event: event | |
| 11 | + .clearfix | |
| 12 | + - elsif event.note? | |
| 13 | + = render "events/event/note", event: event | |
| 14 | + - else | |
| 15 | + = render "events/event/common", event: event | |
| 15 | 16 | ... | ... |
app/views/help/api.html.haml
| ... | ... | @@ -21,6 +21,8 @@ |
| 21 | 21 | = link_to "Milestones", "#milestones", 'data-toggle' => 'tab' |
| 22 | 22 | %li |
| 23 | 23 | = link_to "Notes", "#notes", 'data-toggle' => 'tab' |
| 24 | + %li | |
| 25 | + = link_to "System Hooks", "#system_hooks", 'data-toggle' => 'tab' | |
| 24 | 26 | |
| 25 | 27 | .tab-content |
| 26 | 28 | .tab-pane.active#README |
| ... | ... | @@ -103,3 +105,12 @@ |
| 103 | 105 | .file_content.wiki |
| 104 | 106 | = preserve do |
| 105 | 107 | = markdown File.read(Rails.root.join("doc", "api", "notes.md")) |
| 108 | + | |
| 109 | + .tab-pane#system_hooks | |
| 110 | + .file_holder | |
| 111 | + .file_title | |
| 112 | + %i.icon-file | |
| 113 | + System Hooks | |
| 114 | + .file_content.wiki | |
| 115 | + = preserve do | |
| 116 | + = markdown File.read(Rails.root.join("doc", "api", "system_hooks.md")) | ... | ... |
app/views/help/index.html.haml
| ... | ... | @@ -22,7 +22,7 @@ |
| 22 | 22 | = mail_to Gitlab.config.gitlab.support_email, "support contact" |
| 23 | 23 | %li |
| 24 | 24 | Use the |
| 25 | - = link_to "search bar", '#', onclick: "$("#search").focus();" | |
| 25 | + = link_to "search bar", '#', onclick: "$('#search').focus();" | |
| 26 | 26 | on the top of this page |
| 27 | 27 | %li |
| 28 | 28 | Ask in our | ... | ... |
app/views/layouts/public.html.haml
app/views/projects/_form.html.haml
| ... | ... | @@ -9,11 +9,19 @@ |
| 9 | 9 | Project name is |
| 10 | 10 | .input |
| 11 | 11 | = f.text_field :name, placeholder: "Example Project", class: "xxlarge" |
| 12 | + | |
| 13 | + | |
| 12 | 14 | - unless @repository.heads.empty? |
| 13 | 15 | .clearfix |
| 14 | 16 | = f.label :default_branch, "Default Branch" |
| 15 | 17 | .input= f.select(:default_branch, @repository.heads.map(&:name), {}, style: "width:210px;") |
| 16 | 18 | |
| 19 | + .clearfix | |
| 20 | + = f.label :description do | |
| 21 | + Project description | |
| 22 | + %span.light (optional) | |
| 23 | + .input | |
| 24 | + = f.text_area :description, placeholder: "awesome project", class: "xxlarge", rows: 3, maxlength: 250 | |
| 17 | 25 | |
| 18 | 26 | %fieldset.features |
| 19 | 27 | %legend Features: | ... | ... |
app/views/projects/show.html.haml
| 1 | 1 | = render "project_head" |
| 2 | 2 | = render 'clone_panel' |
| 3 | 3 | = render "events/event_last_push", event: @last_push |
| 4 | -.content_list= render @events | |
| 5 | -.loading.hide | |
| 6 | 4 | |
| 5 | +.row | |
| 6 | + .span9 | |
| 7 | + .content_list= render @events | |
| 8 | + .loading.hide | |
| 9 | + .span3 | |
| 10 | + .ui-box.white | |
| 11 | + .padded | |
| 12 | + %h3.page_title | |
| 13 | + = @project.name | |
| 14 | + - if @project.description.present? | |
| 15 | + %p.light= @project.description | |
| 16 | + | |
| 17 | + %hr | |
| 18 | + %p | |
| 19 | + Access level: | |
| 20 | + - if @project.public | |
| 21 | + %span.cblue | |
| 22 | + %i.icon-share | |
| 23 | + Public | |
| 24 | + - else | |
| 25 | + %span.cgreen | |
| 26 | + %i.icon-lock | |
| 27 | + Private | |
| 28 | + | |
| 29 | + %p Repo Size: #{@project.repository.size} MB | |
| 30 | + %p Created at: #{@project.created_at.stamp('Aug 22, 2013')} | |
| 31 | + %p Owner: #{link_to @project.owner_name, @project.owner} | |
| 7 | 32 | :javascript |
| 8 | 33 | $(function(){ Pager.init(20); }); | ... | ... |
app/views/public/projects/index.html.haml
| 1 | 1 | %h3.page_title |
| 2 | - Projects | |
| 2 | + Projects (#{@projects.total_count}) | |
| 3 | 3 | %small with read-only access |
| 4 | 4 | %hr |
| 5 | 5 | |
| 6 | -%ul.unstyled | |
| 7 | - - @projects.each do |project| | |
| 8 | - %li.clearfix | |
| 9 | - %h5 | |
| 10 | - %i.icon-share | |
| 11 | - = project.name_with_namespace | |
| 12 | - .pull-right | |
| 13 | - %pre.dark.tiny git clone #{project.http_url_to_repo} | |
| 6 | +.public-projects | |
| 7 | + %ul.unstyled | |
| 8 | + - @projects.each do |project| | |
| 9 | + %li.clearfix | |
| 10 | + %h5 | |
| 11 | + %i.icon-share | |
| 12 | + = project.name_with_namespace | |
| 13 | + .pull-right | |
| 14 | + %pre.dark.tiny git clone #{project.http_url_to_repo} | |
| 15 | + %p.description | |
| 16 | + = project.description | |
| 17 | + - unless @projects.present? | |
| 18 | + %h3.nothing_here_message No public projects | |
| 14 | 19 | |
| 15 | - - unless @projects.present? | |
| 16 | - %h3.nothing_here_message No public projects | |
| 17 | - | |
| 18 | -= paginate @projects, theme: "admin" | |
| 20 | + = paginate @projects, theme: "admin" | ... | ... |
app/views/tree/_tree.html.haml
| ... | ... | @@ -11,9 +11,6 @@ |
| 11 | 11 | - else |
| 12 | 12 | = link_to title, '#' |
| 13 | 13 | |
| 14 | -.clear | |
| 15 | -%div.tree_progress | |
| 16 | - | |
| 17 | 14 | %div#tree-content-holder.tree-content-holder |
| 18 | 15 | - if tree.is_blob? |
| 19 | 16 | = render "tree/blob", blob: tree |
| ... | ... | @@ -40,6 +37,8 @@ |
| 40 | 37 | - if tree.readme |
| 41 | 38 | = render "tree/readme", readme: tree.readme |
| 42 | 39 | |
| 40 | +%div.tree_progress | |
| 41 | + | |
| 43 | 42 | - unless tree.is_blob? |
| 44 | 43 | :javascript |
| 45 | 44 | // Load last commit log for each file in tree | ... | ... |
| ... | ... | @@ -0,0 +1,72 @@ |
| 1 | +set :domain, 'set application domain here' | |
| 2 | +set :db_adapter, 'mysql' # or postgres | |
| 3 | +set :mount_point, '/' | |
| 4 | +set :application, 'gitlabhq' | |
| 5 | +set :user, 'git' | |
| 6 | +set :rails_env, 'production' | |
| 7 | +set :deploy_to, "/home/#{user}/apps/#{application}" | |
| 8 | +set :bundle_without, %w[development test] + (%w[mysql postgres] - [db_adapter]) | |
| 9 | +set :asset_env, "RAILS_GROUPS=assets RAILS_RELATIVE_URL_ROOT=#{mount_point.sub /\/+\Z/, ''}" | |
| 10 | + | |
| 11 | +set :use_sudo, false | |
| 12 | +default_run_options[:pty] = true | |
| 13 | + | |
| 14 | +# Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none` | |
| 15 | +set :scm, :git | |
| 16 | +set :repository, "git@#{domain}:#{application}.git" | |
| 17 | +set :deploy_via, :remote_cache | |
| 18 | + | |
| 19 | +# Alternatively, you can deploy via copy, if you don't have gitlab in git | |
| 20 | +#set :scm, :none | |
| 21 | +#set :repository, '.' | |
| 22 | +#set :deploy_via, :copy | |
| 23 | + | |
| 24 | +server domain, :app, :web, :db, primary: true | |
| 25 | + | |
| 26 | +namespace :foreman do | |
| 27 | + desc 'Export the Procfile to Ubuntu upstart scripts' | |
| 28 | + task :export, roles: :app do | |
| 29 | + foreman_export = "foreman export upstart /etc/init -f Procfile -a #{application} -u #{user} -l #{shared_path}/log/foreman" | |
| 30 | + run "cd #{release_path} && #{sudo} #{fetch :bundle_cmd, 'bundle'} exec #{foreman_export}" | |
| 31 | + end | |
| 32 | + | |
| 33 | + desc 'Start the application services' | |
| 34 | + task :start, roles: :app do | |
| 35 | + run "#{sudo} service #{application} start" | |
| 36 | + end | |
| 37 | + | |
| 38 | + desc 'Stop the application services' | |
| 39 | + task :stop, roles: :app do | |
| 40 | + run "#{sudo} service #{application} stop" | |
| 41 | + end | |
| 42 | + | |
| 43 | + desc 'Restart the application services' | |
| 44 | + task :restart, roles: :app do | |
| 45 | + run "#{sudo} service #{application} restart" | |
| 46 | + end | |
| 47 | +end | |
| 48 | + | |
| 49 | +namespace :deploy do | |
| 50 | + desc 'Start the application services' | |
| 51 | + task :start, roles: :app do | |
| 52 | + foreman.start | |
| 53 | + end | |
| 54 | + | |
| 55 | + desc 'Stop the application services' | |
| 56 | + task :stop, roles: :app do | |
| 57 | + foreman.stop | |
| 58 | + end | |
| 59 | + | |
| 60 | + desc 'Restart the application services' | |
| 61 | + task :restart, roles: :app do | |
| 62 | + foreman.restart | |
| 63 | + end | |
| 64 | +end | |
| 65 | + | |
| 66 | +after 'deploy:cold' do | |
| 67 | + run "cd #{release_path} && #{rake} gitlab:setup force=yes RAILS_ENV=#{rails_env}" | |
| 68 | + deploy.restart | |
| 69 | +end | |
| 70 | + | |
| 71 | +after 'deploy:update', 'foreman:export' # Export foreman scripts | |
| 72 | +#after 'deploy:update', 'foreman:restart' # Restart application scripts | ... | ... |
config/environments/production.rb
| ... | ... | @@ -40,7 +40,7 @@ Gitlab::Application.configure do |
| 40 | 40 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) |
| 41 | 41 | |
| 42 | 42 | # Use a different cache store in production |
| 43 | - config.cache_store = :memory_store | |
| 43 | + config.cache_store = :redis_store | |
| 44 | 44 | |
| 45 | 45 | # Enable serving of images, stylesheets, and JavaScripts from an asset server |
| 46 | 46 | # config.action_controller.asset_host = "http://assets.example.com" | ... | ... |
config/initializers/4_sidekiq.rb
| ... | ... | @@ -4,19 +4,19 @@ config_file = Rails.root.join('config', 'resque.yml') |
| 4 | 4 | resque_url = if File.exists?(config_file) |
| 5 | 5 | YAML.load_file(config_file)[Rails.env] |
| 6 | 6 | else |
| 7 | - "localhost:6379" | |
| 7 | + "redis://localhost:6379" | |
| 8 | 8 | end |
| 9 | 9 | |
| 10 | 10 | Sidekiq.configure_server do |config| |
| 11 | 11 | config.redis = { |
| 12 | - url: "redis://#{resque_url}", | |
| 12 | + url: resque_url, | |
| 13 | 13 | namespace: 'resque:gitlab' |
| 14 | 14 | } |
| 15 | 15 | end |
| 16 | 16 | |
| 17 | 17 | Sidekiq.configure_client do |config| |
| 18 | 18 | config.redis = { |
| 19 | - url: "redis://#{resque_url}", | |
| 19 | + url: resque_url, | |
| 20 | 20 | namespace: 'resque:gitlab' |
| 21 | 21 | } |
| 22 | 22 | end | ... | ... |
config/resque.yml.example
config/routes.rb
| ... | ... | @@ -166,7 +166,7 @@ Gitlab::Application.routes.draw do |
| 166 | 166 | # |
| 167 | 167 | # Project Area |
| 168 | 168 | # |
| 169 | - resources :projects, constraints: { id: /[a-zA-Z.0-9_\-\/]+/ }, except: [:new, :create, :index], path: "/" do | |
| 169 | + resources :projects, constraints: { id: /(?:[a-zA-Z.0-9_\-]+\/)?[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index], path: "/" do | |
| 170 | 170 | member do |
| 171 | 171 | get "wall" |
| 172 | 172 | get "files" |
| ... | ... | @@ -175,10 +175,10 @@ Gitlab::Application.routes.draw do |
| 175 | 175 | resources :blob, only: [:show], constraints: {id: /.+/} |
| 176 | 176 | resources :tree, only: [:show, :edit, :update], constraints: {id: /.+/} |
| 177 | 177 | resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/} |
| 178 | - resources :commits, only: [:show], constraints: {id: /.+/} | |
| 178 | + resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/} | |
| 179 | 179 | resources :compare, only: [:index, :create] |
| 180 | 180 | resources :blame, only: [:show], constraints: {id: /.+/} |
| 181 | - resources :graph, only: [:show], constraints: {id: /.+/} | |
| 181 | + resources :graph, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/} | |
| 182 | 182 | match "/compare/:from...:to" => "compare#show", as: "compare", |
| 183 | 183 | :via => [:get, :post], constraints: {from: /.+/, to: /.+/} |
| 184 | 184 | ... | ... |
doc/api/README.md
| ... | ... | @@ -31,13 +31,10 @@ The API uses JSON to serialize data. You don't need to specify `.json` at the en |
| 31 | 31 | |
| 32 | 32 | ## Status codes |
| 33 | 33 | |
| 34 | -API requests return different status codes according to | |
| 35 | - | |
| 36 | -The API is designed to provide status codes according to the context and how the request | |
| 37 | -is handled. For example if a `GET` request is successful a status code `200 Ok` | |
| 38 | -is returned. The API is designed to be RESTful. | |
| 39 | - | |
| 40 | -The following list gives an overview of how the API functions are designed. | |
| 34 | +The API is designed to return different status codes according to context and action. In this way | |
| 35 | +if a request results in an error the caller is able to get insight into what went wrong, e.g. | |
| 36 | +status code `400 Bad Request` is returned if a required attribute is missing from the request. | |
| 37 | +The following list gives an overview of how the API functions generally behave. | |
| 41 | 38 | |
| 42 | 39 | API request types: |
| 43 | 40 | |
| ... | ... | @@ -58,7 +55,7 @@ Return values: |
| 58 | 55 | * `403 Forbidden` - The request is not allowed, e.g. the user is not allowed to delete a project |
| 59 | 56 | * `404 Not Found` - A resource could not be accessed, e.g. an ID for a resource could not be found |
| 60 | 57 | * `405 Method Not Allowed` - The request is not supported |
| 61 | -* `409 Conflict` - A conflicting resource already exists, a project with same name already exists | |
| 58 | +* `409 Conflict` - A conflicting resource already exists, e.g. creating a project with a name that already exists | |
| 62 | 59 | * `500 Server Error` - While handling the request something went wrong on the server side |
| 63 | 60 | |
| 64 | 61 | ... | ... |
doc/api/groups.md
| ... | ... | @@ -44,3 +44,14 @@ Parameters: |
| 44 | 44 | + `name` (required) - The name of the group |
| 45 | 45 | + `path` (required) - The path of the group |
| 46 | 46 | |
| 47 | +## Transfer project to group | |
| 48 | + | |
| 49 | +Transfer a project to the Group namespace. Available only for admin | |
| 50 | + | |
| 51 | +``` | |
| 52 | +POST /groups/:id/projects/:project_id | |
| 53 | +``` | |
| 54 | + | |
| 55 | +Parameters: | |
| 56 | ++ `id` (required) - The ID of a group | |
| 57 | ++ `project_id (required) - The ID of a project | ... | ... |
doc/api/projects.md
| ... | ... | @@ -115,11 +115,9 @@ Parameters: |
| 115 | 115 | + `merge_requests_enabled` (optional) - enabled by default |
| 116 | 116 | + `wiki_enabled` (optional) - enabled by default |
| 117 | 117 | |
| 118 | +**Project access levels** | |
| 118 | 119 | |
| 119 | -## Project access levels | |
| 120 | - | |
| 121 | -The project access levels are defined in the `user_project` class. Currently, 4 | |
| 122 | -levels are recoginized: | |
| 120 | +The project access levels are defined in the `user_project.rb` class. Currently, these levels are recoginized: | |
| 123 | 121 | |
| 124 | 122 | ``` |
| 125 | 123 | GUEST = 10 |
| ... | ... | @@ -129,7 +127,30 @@ levels are recoginized: |
| 129 | 127 | ``` |
| 130 | 128 | |
| 131 | 129 | |
| 132 | -## List project team members | |
| 130 | +### Create project for user | |
| 131 | + | |
| 132 | +Creates a new project owned by user. Available only for admins. | |
| 133 | + | |
| 134 | +``` | |
| 135 | +POST /projects/user/:user_id | |
| 136 | +``` | |
| 137 | + | |
| 138 | +Parameters: | |
| 139 | + | |
| 140 | ++ `user_id` (required) - user_id of owner | |
| 141 | ++ `name` (required) - new project name | |
| 142 | ++ `description` (optional) - short project description | |
| 143 | ++ `default_branch` (optional) - 'master' by default | |
| 144 | ++ `issues_enabled` (optional) - enabled by default | |
| 145 | ++ `wall_enabled` (optional) - enabled by default | |
| 146 | ++ `merge_requests_enabled` (optional) - enabled by default | |
| 147 | ++ `wiki_enabled` (optional) - enabled by default | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | +## Team members | |
| 152 | + | |
| 153 | +### List project team members | |
| 133 | 154 | |
| 134 | 155 | Get a list of project team members. |
| 135 | 156 | |
| ... | ... | @@ -140,14 +161,12 @@ GET /projects/:id/members |
| 140 | 161 | Parameters: |
| 141 | 162 | |
| 142 | 163 | + `id` (required) - The ID or NAME of a project |
| 143 | -+ `query` - Query string | |
| 144 | - | |
| 164 | ++ `query` (optional) - Query string to search for members | |
| 145 | 165 | |
| 146 | -## Team members | |
| 147 | 166 | |
| 148 | 167 | ### Get project team member |
| 149 | 168 | |
| 150 | -Get a project team member. | |
| 169 | +Gets a project team member. | |
| 151 | 170 | |
| 152 | 171 | ``` |
| 153 | 172 | GET /projects/:id/members/:user_id |
| ... | ... | @@ -175,7 +194,7 @@ Parameters: |
| 175 | 194 | |
| 176 | 195 | Adds a user to a project team. This is an idempotent method and can be called multiple times |
| 177 | 196 | with the same parameters. Adding team membership to a user that is already a member does not |
| 178 | -affect the membership. | |
| 197 | +affect the existing membership. | |
| 179 | 198 | |
| 180 | 199 | ``` |
| 181 | 200 | POST /projects/:id/members |
| ... | ... | @@ -190,7 +209,7 @@ Parameters: |
| 190 | 209 | |
| 191 | 210 | ### Edit project team member |
| 192 | 211 | |
| 193 | -Update project team member to specified access level. | |
| 212 | +Updates project team member to a specified access level. | |
| 194 | 213 | |
| 195 | 214 | ``` |
| 196 | 215 | PUT /projects/:id/members/:user_id |
| ... | ... | @@ -398,81 +417,90 @@ Returns values: |
| 398 | 417 | + `404 Not Found` if project with id or the branch with `ref_name` not found |
| 399 | 418 | |
| 400 | 419 | |
| 401 | -## Snippets | |
| 402 | 420 | |
| 403 | -### List snippets | |
| 421 | +## Deploy Keys | |
| 422 | + | |
| 423 | +### List deploy keys | |
| 404 | 424 | |
| 405 | -Lists the snippets of a project. | |
| 425 | +Get a list of a project's deploy keys. | |
| 406 | 426 | |
| 407 | 427 | ``` |
| 408 | -GET /projects/:id/snippets | |
| 428 | +GET /projects/:id/keys | |
| 409 | 429 | ``` |
| 410 | 430 | |
| 411 | 431 | Parameters: |
| 412 | 432 | |
| 413 | 433 | + `id` (required) - The ID of the project |
| 414 | 434 | |
| 415 | - | |
| 416 | -### List single snippet | |
| 417 | - | |
| 418 | -Lists a single snippet of a project | |
| 419 | - | |
| 420 | -``` | |
| 421 | -GET /projects/:id/snippets/:snippet_id | |
| 435 | +```json | |
| 436 | +[ | |
| 437 | + { | |
| 438 | + "id": 1, | |
| 439 | + "title" : "Public key" | |
| 440 | + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | |
| 441 | + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | |
| 442 | + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", | |
| 443 | + }, | |
| 444 | + { | |
| 445 | + "id": 3, | |
| 446 | + "title" : "Another Public key" | |
| 447 | + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | |
| 448 | + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | |
| 449 | + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | |
| 450 | + } | |
| 451 | +] | |
| 422 | 452 | ``` |
| 423 | 453 | |
| 424 | -Parameters: | |
| 425 | - | |
| 426 | -+ `id` (required) - The ID of the project | |
| 427 | -+ `snippet_id` (required) - The ID of the snippet | |
| 428 | 454 | |
| 455 | +### Single deploy key | |
| 429 | 456 | |
| 430 | -### Create snippet | |
| 431 | - | |
| 432 | -Creates a new project snippet. | |
| 457 | +Get a single key. | |
| 433 | 458 | |
| 434 | 459 | ``` |
| 435 | -POST /projects/:id/snippets | |
| 460 | +GET /projects/:id/keys/:key_id | |
| 436 | 461 | ``` |
| 437 | 462 | |
| 438 | 463 | Parameters: |
| 439 | 464 | |
| 440 | 465 | + `id` (required) - The ID of the project |
| 441 | -+ `title` (required) - The title of the new snippet | |
| 442 | -+ `file_name` (required) - The file name of the snippet | |
| 443 | -+ `code` (required) - The content of the snippet | |
| 444 | -+ `lifetime` (optional) - The expiration date of a snippet | |
| 466 | ++ `key_id` (required) - The ID of the deploy key | |
| 445 | 467 | |
| 468 | +```json | |
| 469 | +{ | |
| 470 | + "id": 1, | |
| 471 | + "title" : "Public key" | |
| 472 | + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | |
| 473 | + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | |
| 474 | + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | |
| 475 | +} | |
| 476 | +``` | |
| 446 | 477 | |
| 447 | -### Update snippet | |
| 448 | 478 | |
| 449 | -Updates an existing project snippet. | |
| 479 | +### Add deploy key | |
| 480 | + | |
| 481 | +Creates a new deploy key for a project. | |
| 450 | 482 | |
| 451 | 483 | ``` |
| 452 | -PUT /projects/:id/snippets/:snippet_id | |
| 484 | +POST /projects/:id/keys | |
| 453 | 485 | ``` |
| 454 | 486 | |
| 455 | 487 | Parameters: |
| 456 | 488 | |
| 457 | 489 | + `id` (required) - The ID of the project |
| 458 | -+ `snippet_id` (required) - The id of the project snippet | |
| 459 | -+ `title` (optional) - The new title of the project snippet | |
| 460 | -+ `file_name` (optional) - The new file name of the project snippet | |
| 461 | -+ `lifetime` (optional) - The new expiration date of the snippet | |
| 462 | -+ `code` (optional) - The content of the snippet | |
| 490 | ++ `title` (required) - New deploy key's title | |
| 491 | ++ `key` (required) - New deploy key | |
| 463 | 492 | |
| 464 | 493 | |
| 465 | -## Delete snippet | |
| 494 | +### Delete deploy key | |
| 466 | 495 | |
| 467 | -Deletes a project snippet. This is an idempotent function call and returns `200 Ok` | |
| 468 | -even if the snippet with the id is not available. | |
| 496 | +Delete a deploy key from a project | |
| 469 | 497 | |
| 470 | 498 | ``` |
| 471 | -DELETE /projects/:id/snippets/:snippet_id | |
| 499 | +DELETE /projects/:id/keys/:key_id | |
| 472 | 500 | ``` |
| 473 | 501 | |
| 474 | -Paramaters: | |
| 502 | +Parameters: | |
| 475 | 503 | |
| 476 | 504 | + `id` (required) - The ID of the project |
| 477 | -+ `snippet_id` (required) - The ID of the snippet | |
| 505 | ++ `key_id` (required) - The ID of the deploy key | |
| 478 | 506 | ... | ... |
doc/api/snippets.md
| ... | ... | @@ -46,7 +46,7 @@ Parameters: |
| 46 | 46 | |
| 47 | 47 | ## Create new snippet |
| 48 | 48 | |
| 49 | -Creates a new project snippet. | |
| 49 | +Creates a new project snippet. The user must have permission to create new snippets. | |
| 50 | 50 | |
| 51 | 51 | ``` |
| 52 | 52 | POST /projects/:id/snippets |
| ... | ... | @@ -61,9 +61,9 @@ Parameters: |
| 61 | 61 | + `code` (required) - The content of a snippet |
| 62 | 62 | |
| 63 | 63 | |
| 64 | -## Edit snippet | |
| 64 | +## Update snippet | |
| 65 | 65 | |
| 66 | -Updates an existing project snippet. | |
| 66 | +Updates an existing project snippet. The user must have permission to change an existing snippet. | |
| 67 | 67 | |
| 68 | 68 | ``` |
| 69 | 69 | PUT /projects/:id/snippets/:snippet_id |
| ... | ... | @@ -96,7 +96,7 @@ Parameters: |
| 96 | 96 | |
| 97 | 97 | ## Snippet content |
| 98 | 98 | |
| 99 | -Get a raw project snippet. | |
| 99 | +Returns the raw project snippet as plain text. | |
| 100 | 100 | |
| 101 | 101 | ``` |
| 102 | 102 | GET /projects/:id/snippets/:snippet_id/raw | ... | ... |
| ... | ... | @@ -0,0 +1,47 @@ |
| 1 | +All methods require admin authorization. | |
| 2 | + | |
| 3 | +## List system hooks | |
| 4 | + | |
| 5 | +Get list of system hooks | |
| 6 | + | |
| 7 | +``` | |
| 8 | +GET /hooks | |
| 9 | +``` | |
| 10 | + | |
| 11 | +Will return hooks with status `200 OK` on success, or `404 Not found` on fail. | |
| 12 | + | |
| 13 | +## Add new system hook hook | |
| 14 | + | |
| 15 | +``` | |
| 16 | +POST /hooks | |
| 17 | +``` | |
| 18 | + | |
| 19 | +Parameters: | |
| 20 | + | |
| 21 | ++ `url` (required) - The hook URL | |
| 22 | + | |
| 23 | +Will return status `201 Created` on success, or `404 Not found` on fail. | |
| 24 | + | |
| 25 | +## Test system hook | |
| 26 | + | |
| 27 | +``` | |
| 28 | +GET /hooks/:id | |
| 29 | +``` | |
| 30 | + | |
| 31 | +Parameters: | |
| 32 | + | |
| 33 | ++ `id` (required) - The ID of hook | |
| 34 | + | |
| 35 | +Will return hook with status `200 OK` on success, or `404 Not found` on fail. | |
| 36 | + | |
| 37 | +## Delete system hook | |
| 38 | + | |
| 39 | +``` | |
| 40 | +DELETE /hooks/:id | |
| 41 | +``` | |
| 42 | + | |
| 43 | +Parameters: | |
| 44 | + | |
| 45 | ++ `id` (required) - The ID of hook | |
| 46 | + | |
| 47 | +Will return status `200 OK` on success, or `404 Not found` on fail. | |
| 0 | 48 | \ No newline at end of file | ... | ... |
doc/api/users.md
| ... | ... | @@ -235,6 +235,23 @@ Parameters: |
| 235 | 235 | + `key` (required) - new SSH key |
| 236 | 236 | |
| 237 | 237 | |
| 238 | +## Add SSH key for user | |
| 239 | + | |
| 240 | +Create new key owned by specified user. Available only for admin | |
| 241 | + | |
| 242 | +``` | |
| 243 | +POST /users/:id/keys | |
| 244 | +``` | |
| 245 | + | |
| 246 | +Parameters: | |
| 247 | + | |
| 248 | ++ `id` (required) - id of specified user | |
| 249 | ++ `title` (required) - new SSH Key's title | |
| 250 | ++ `key` (required) - new SSH key | |
| 251 | + | |
| 252 | +Will return created key with status `201 Created` on success, or `404 Not | |
| 253 | +found` on fail. | |
| 254 | + | |
| 238 | 255 | ## Delete SSH key |
| 239 | 256 | |
| 240 | 257 | Deletes key owned by currently authenticated user. This is an idempotent function and calling it on a key that is already | ... | ... |
doc/install/installation.md
| ... | ... | @@ -288,7 +288,7 @@ a different host, you can configure its connection string via the |
| 288 | 288 | `config/resque.yml` file. |
| 289 | 289 | |
| 290 | 290 | # example |
| 291 | - production: redis.example.tld:6379 | |
| 291 | + production: redis://redis.example.tld:6379 | |
| 292 | 292 | |
| 293 | 293 | ## Custom SSH Connection |
| 294 | 294 | ... | ... |
features/steps/project/project_network_graph.rb
| ... | ... | @@ -27,6 +27,7 @@ class ProjectNetworkGraph < Spinach::FeatureSteps |
| 27 | 27 | |
| 28 | 28 | And 'I switch ref to "stable"' do |
| 29 | 29 | page.select 'stable', :from => 'ref' |
| 30 | + sleep 2 | |
| 30 | 31 | end |
| 31 | 32 | |
| 32 | 33 | And 'page should select "stable" in select box' do |
| ... | ... | @@ -44,6 +45,7 @@ class ProjectNetworkGraph < Spinach::FeatureSteps |
| 44 | 45 | fill_in 'q', :with => '98d6492' |
| 45 | 46 | find('button').click |
| 46 | 47 | end |
| 48 | + sleep 2 | |
| 47 | 49 | end |
| 48 | 50 | |
| 49 | 51 | And 'page should have "v2.1.0" on graph' do | ... | ... |
lib/api.rb
lib/api/groups.rb
| ... | ... | @@ -56,6 +56,24 @@ module Gitlab |
| 56 | 56 | not_found! |
| 57 | 57 | end |
| 58 | 58 | end |
| 59 | + | |
| 60 | + # Transfer a project to the Group namespace | |
| 61 | + # | |
| 62 | + # Parameters: | |
| 63 | + # id - group id | |
| 64 | + # project_id - project id | |
| 65 | + # Example Request: | |
| 66 | + # POST /groups/:id/projects/:project_id | |
| 67 | + post ":id/projects/:project_id" do | |
| 68 | + authenticated_as_admin! | |
| 69 | + @group = Group.find(params[:id]) | |
| 70 | + project = Project.find(params[:project_id]) | |
| 71 | + if project.transfer(@group) | |
| 72 | + present @group | |
| 73 | + else | |
| 74 | + not_found! | |
| 75 | + end | |
| 76 | + end | |
| 59 | 77 | end |
| 60 | 78 | end |
| 61 | 79 | end | ... | ... |
lib/api/merge_requests.rb
lib/api/projects.rb
| ... | ... | @@ -64,6 +64,38 @@ module Gitlab |
| 64 | 64 | end |
| 65 | 65 | end |
| 66 | 66 | |
| 67 | + # Create new project for a specified user. Only available to admin users. | |
| 68 | + # | |
| 69 | + # Parameters: | |
| 70 | + # user_id (required) - The ID of a user | |
| 71 | + # name (required) - name for new project | |
| 72 | + # description (optional) - short project description | |
| 73 | + # default_branch (optional) - 'master' by default | |
| 74 | + # issues_enabled (optional) - enabled by default | |
| 75 | + # wall_enabled (optional) - enabled by default | |
| 76 | + # merge_requests_enabled (optional) - enabled by default | |
| 77 | + # wiki_enabled (optional) - enabled by default | |
| 78 | + # Example Request | |
| 79 | + # POST /projects/user/:user_id | |
| 80 | + post "user/:user_id" do | |
| 81 | + authenticated_as_admin! | |
| 82 | + user = User.find(params[:user_id]) | |
| 83 | + attrs = attributes_for_keys [:name, | |
| 84 | + :description, | |
| 85 | + :default_branch, | |
| 86 | + :issues_enabled, | |
| 87 | + :wall_enabled, | |
| 88 | + :merge_requests_enabled, | |
| 89 | + :wiki_enabled] | |
| 90 | + @project = ::Projects::CreateContext.new(user, attrs).execute | |
| 91 | + if @project.saved? | |
| 92 | + present @project, with: Entities::Project | |
| 93 | + else | |
| 94 | + not_found! | |
| 95 | + end | |
| 96 | + end | |
| 97 | + | |
| 98 | + | |
| 67 | 99 | # Get a project team members |
| 68 | 100 | # |
| 69 | 101 | # Parameters: |
| ... | ... | @@ -471,6 +503,49 @@ module Gitlab |
| 471 | 503 | present tree.data |
| 472 | 504 | end |
| 473 | 505 | |
| 506 | + # Get a specific project's keys | |
| 507 | + # | |
| 508 | + # Example Request: | |
| 509 | + # GET /projects/:id/keys | |
| 510 | + get ":id/keys" do | |
| 511 | + present user_project.deploy_keys, with: Entities::SSHKey | |
| 512 | + end | |
| 513 | + | |
| 514 | + # Get single key owned by currently authenticated user | |
| 515 | + # | |
| 516 | + # Example Request: | |
| 517 | + # GET /projects/:id/keys/:id | |
| 518 | + get ":id/keys/:key_id" do | |
| 519 | + key = user_project.deploy_keys.find params[:key_id] | |
| 520 | + present key, with: Entities::SSHKey | |
| 521 | + end | |
| 522 | + | |
| 523 | + # Add new ssh key to currently authenticated user | |
| 524 | + # | |
| 525 | + # Parameters: | |
| 526 | + # key (required) - New SSH Key | |
| 527 | + # title (required) - New SSH Key's title | |
| 528 | + # Example Request: | |
| 529 | + # POST /projects/:id/keys | |
| 530 | + post ":id/keys" do | |
| 531 | + attrs = attributes_for_keys [:title, :key] | |
| 532 | + key = user_project.deploy_keys.new attrs | |
| 533 | + if key.save | |
| 534 | + present key, with: Entities::SSHKey | |
| 535 | + else | |
| 536 | + not_found! | |
| 537 | + end | |
| 538 | + end | |
| 539 | + | |
| 540 | + # Delete existed ssh key of currently authenticated user | |
| 541 | + # | |
| 542 | + # Example Request: | |
| 543 | + # DELETE /projects/:id/keys/:id | |
| 544 | + delete ":id/keys/:key_id" do | |
| 545 | + key = user_project.deploy_keys.find params[:key_id] | |
| 546 | + key.delete | |
| 547 | + end | |
| 548 | + | |
| 474 | 549 | end |
| 475 | 550 | end |
| 476 | 551 | end | ... | ... |
| ... | ... | @@ -0,0 +1,60 @@ |
| 1 | +module Gitlab | |
| 2 | + # Hooks API | |
| 3 | + class SystemHooks < Grape::API | |
| 4 | + before { authenticated_as_admin! } | |
| 5 | + | |
| 6 | + resource :hooks do | |
| 7 | + # Get the list of system hooks | |
| 8 | + # | |
| 9 | + # Example Request: | |
| 10 | + # GET /hooks | |
| 11 | + get do | |
| 12 | + @hooks = SystemHook.all | |
| 13 | + present @hooks, with: Entities::Hook | |
| 14 | + end | |
| 15 | + | |
| 16 | + # Create new system hook | |
| 17 | + # | |
| 18 | + # Parameters: | |
| 19 | + # url (required) - url for system hook | |
| 20 | + # Example Request | |
| 21 | + # POST /hooks | |
| 22 | + post do | |
| 23 | + attrs = attributes_for_keys [:url] | |
| 24 | + @hook = SystemHook.new attrs | |
| 25 | + if @hook.save | |
| 26 | + present @hook, with: Entities::Hook | |
| 27 | + else | |
| 28 | + not_found! | |
| 29 | + end | |
| 30 | + end | |
| 31 | + | |
| 32 | + # Test a hook | |
| 33 | + # | |
| 34 | + # Example Request | |
| 35 | + # GET /hooks/:id | |
| 36 | + get ":id" do | |
| 37 | + @hook = SystemHook.find(params[:id]) | |
| 38 | + data = { | |
| 39 | + event_name: "project_create", | |
| 40 | + name: "Ruby", | |
| 41 | + path: "ruby", | |
| 42 | + project_id: 1, | |
| 43 | + owner_name: "Someone", | |
| 44 | + owner_email: "example@gitlabhq.com" | |
| 45 | + } | |
| 46 | + @hook.execute(data) | |
| 47 | + data | |
| 48 | + end | |
| 49 | + | |
| 50 | + # Delete a hook | |
| 51 | + # | |
| 52 | + # Example Request: | |
| 53 | + # DELETE /hooks/:id | |
| 54 | + delete ":id" do | |
| 55 | + @hook = SystemHook.find(params[:id]) | |
| 56 | + @hook.destroy | |
| 57 | + end | |
| 58 | + end | |
| 59 | + end | |
| 60 | +end | |
| 0 | 61 | \ No newline at end of file | ... | ... |
lib/api/users.rb
| ... | ... | @@ -81,6 +81,26 @@ module Gitlab |
| 81 | 81 | end |
| 82 | 82 | end |
| 83 | 83 | |
| 84 | + # Add ssh key to a specified user. Only available to admin users. | |
| 85 | + # | |
| 86 | + # Parameters: | |
| 87 | + # id (required) - The ID of a user | |
| 88 | + # key (required) - New SSH Key | |
| 89 | + # title (required) - New SSH Key's title | |
| 90 | + # Example Request: | |
| 91 | + # POST /users/:id/keys | |
| 92 | + post ":id/keys" do | |
| 93 | + authenticated_as_admin! | |
| 94 | + user = User.find(params[:id]) | |
| 95 | + attrs = attributes_for_keys [:title, :key] | |
| 96 | + key = user.keys.new attrs | |
| 97 | + if key.save | |
| 98 | + present key, with: Entities::SSHKey | |
| 99 | + else | |
| 100 | + not_found! | |
| 101 | + end | |
| 102 | + end | |
| 103 | + | |
| 84 | 104 | # Delete user. Available only for admin |
| 85 | 105 | # |
| 86 | 106 | # Example Request: | ... | ... |
lib/extracts_path.rb
| ... | ... | @@ -105,12 +105,6 @@ module ExtractsPath |
| 105 | 105 | # Automatically renders `not_found!` if a valid tree path could not be |
| 106 | 106 | # resolved (e.g., when a user inserts an invalid path or ref). |
| 107 | 107 | def assign_ref_vars |
| 108 | - # Handle formats embedded in the id | |
| 109 | - if params[:id].ends_with?('.atom') | |
| 110 | - params[:id].gsub!(/\.atom$/, '') | |
| 111 | - request.format = :atom | |
| 112 | - end | |
| 113 | - | |
| 114 | 108 | path = CGI::unescape(request.fullpath.dup) |
| 115 | 109 | |
| 116 | 110 | @ref, @path = extract_ref(path) | ... | ... |
lib/tasks/gitlab/setup.rake
| ... | ... | @@ -7,10 +7,12 @@ namespace :gitlab do |
| 7 | 7 | def setup_db |
| 8 | 8 | warn_user_is_not_gitlab |
| 9 | 9 | |
| 10 | - puts "This will create the necessary database tables and seed the database." | |
| 11 | - puts "You will lose any previous data stored in the database." | |
| 12 | - ask_to_continue | |
| 13 | - puts "" | |
| 10 | + unless ENV['force'] == 'yes' | |
| 11 | + puts "This will create the necessary database tables and seed the database." | |
| 12 | + puts "You will lose any previous data stored in the database." | |
| 13 | + ask_to_continue | |
| 14 | + puts "" | |
| 15 | + end | |
| 14 | 16 | |
| 15 | 17 | Rake::Task["db:setup"].invoke |
| 16 | 18 | Rake::Task["db:seed_fu"].invoke | ... | ... |
spec/controllers/commits_controller_spec.rb
| ... | ... | @@ -13,7 +13,7 @@ describe CommitsController do |
| 13 | 13 | describe "GET show" do |
| 14 | 14 | context "as atom feed" do |
| 15 | 15 | it "should render as atom" do |
| 16 | - get :show, project_id: project.path, id: "master.atom" | |
| 16 | + get :show, project_id: project.path, id: "master", format: "atom" | |
| 17 | 17 | response.should be_success |
| 18 | 18 | response.content_type.should == 'application/atom+xml' |
| 19 | 19 | end | ... | ... |
spec/requests/api/groups_spec.rb
| ... | ... | @@ -100,4 +100,27 @@ describe Gitlab::API do |
| 100 | 100 | end |
| 101 | 101 | end |
| 102 | 102 | end |
| 103 | + | |
| 104 | + describe "POST /groups/:id/projects/:project_id" do | |
| 105 | + let(:project) { create(:project) } | |
| 106 | + before(:each) do | |
| 107 | + project.stub!(:transfer).and_return(true) | |
| 108 | + Project.stub(:find).and_return(project) | |
| 109 | + end | |
| 110 | + | |
| 111 | + | |
| 112 | + context "when authenticated as user" do | |
| 113 | + it "should not transfer project to group" do | |
| 114 | + post api("/groups/#{group1.id}/projects/#{project.id}", user2) | |
| 115 | + response.status.should == 403 | |
| 116 | + end | |
| 117 | + end | |
| 118 | + | |
| 119 | + context "when authenticated as admin" do | |
| 120 | + it "should transfer project to group" do | |
| 121 | + project.should_receive(:transfer) | |
| 122 | + post api("/groups/#{group1.id}/projects/#{project.id}", admin) | |
| 123 | + end | |
| 124 | + end | |
| 125 | + end | |
| 103 | 126 | end | ... | ... |
spec/requests/api/notes_spec.rb
| ... | ... | @@ -105,13 +105,6 @@ describe Gitlab::API do |
| 105 | 105 | response.status.should == 404 |
| 106 | 106 | end |
| 107 | 107 | end |
| 108 | - | |
| 109 | - context "when notable is invalid" do | |
| 110 | - it "should return a 404 error" do | |
| 111 | - get api("/projects/#{project.id}/unknown/#{snippet.id}/notes", user) | |
| 112 | - response.status.should == 404 | |
| 113 | - end | |
| 114 | - end | |
| 115 | 108 | end |
| 116 | 109 | |
| 117 | 110 | describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do |
| ... | ... | @@ -180,12 +173,5 @@ describe Gitlab::API do |
| 180 | 173 | response.status.should == 401 |
| 181 | 174 | end |
| 182 | 175 | end |
| 183 | - | |
| 184 | - context "when noteable is invalid" do | |
| 185 | - it "should return a 404 error" do | |
| 186 | - post api("/projects/#{project.id}/invalid/#{snippet.id}/notes", user) | |
| 187 | - response.status.should == 404 | |
| 188 | - end | |
| 189 | - end | |
| 190 | 176 | end |
| 191 | 177 | end | ... | ... |
spec/requests/api/projects_spec.rb
| ... | ... | @@ -6,11 +6,14 @@ describe Gitlab::API do |
| 6 | 6 | let(:user) { create(:user) } |
| 7 | 7 | let(:user2) { create(:user) } |
| 8 | 8 | let(:user3) { create(:user) } |
| 9 | + let(:admin) { create(:admin) } | |
| 9 | 10 | let!(:project) { create(:project, namespace: user.namespace ) } |
| 10 | 11 | let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } |
| 11 | 12 | let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') } |
| 12 | 13 | let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } |
| 13 | 14 | let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } |
| 15 | + let(:key) { create(:key, project: project) } | |
| 16 | + | |
| 14 | 17 | before { project.team << [user, :reporter] } |
| 15 | 18 | |
| 16 | 19 | describe "GET /projects" do |
| ... | ... | @@ -103,6 +106,46 @@ describe Gitlab::API do |
| 103 | 106 | end |
| 104 | 107 | end |
| 105 | 108 | |
| 109 | + describe "POST /projects/user/:id" do | |
| 110 | + before { admin } | |
| 111 | + | |
| 112 | + it "should create new project without path" do | |
| 113 | + expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1) | |
| 114 | + end | |
| 115 | + | |
| 116 | + it "should not create new project without name" do | |
| 117 | + expect { post api("/projects/user/#{user.id}", admin) }.to_not change {Project.count} | |
| 118 | + end | |
| 119 | + | |
| 120 | + it "should respond with 201 on success" do | |
| 121 | + post api("/projects/user/#{user.id}", admin), name: 'foo' | |
| 122 | + response.status.should == 201 | |
| 123 | + end | |
| 124 | + | |
| 125 | + it "should respond with 404 on failure" do | |
| 126 | + post api("/projects/user/#{user.id}", admin) | |
| 127 | + response.status.should == 404 | |
| 128 | + end | |
| 129 | + | |
| 130 | + it "should assign attributes to project" do | |
| 131 | + project = attributes_for(:project, { | |
| 132 | + description: Faker::Lorem.sentence, | |
| 133 | + default_branch: 'stable', | |
| 134 | + issues_enabled: false, | |
| 135 | + wall_enabled: false, | |
| 136 | + merge_requests_enabled: false, | |
| 137 | + wiki_enabled: false | |
| 138 | + }) | |
| 139 | + | |
| 140 | + post api("/projects/user/#{user.id}", admin), project | |
| 141 | + | |
| 142 | + project.each_pair do |k,v| | |
| 143 | + next if k == :path | |
| 144 | + json_response[k.to_s].should == v | |
| 145 | + end | |
| 146 | + end | |
| 147 | + end | |
| 148 | + | |
| 106 | 149 | describe "GET /projects/:id" do |
| 107 | 150 | it "should return a project by id" do |
| 108 | 151 | get api("/projects/#{project.id}", user) |
| ... | ... | @@ -591,4 +634,59 @@ describe Gitlab::API do |
| 591 | 634 | response.status.should == 400 |
| 592 | 635 | end |
| 593 | 636 | end |
| 637 | + | |
| 638 | + describe "GET /projects/:id/keys" do | |
| 639 | + it "should return array of ssh keys" do | |
| 640 | + project.deploy_keys << key | |
| 641 | + project.save | |
| 642 | + get api("/projects/#{project.id}/keys", user) | |
| 643 | + response.status.should == 200 | |
| 644 | + json_response.should be_an Array | |
| 645 | + json_response.first['title'].should == key.title | |
| 646 | + end | |
| 647 | + end | |
| 648 | + | |
| 649 | + describe "GET /projects/:id/keys/:key_id" do | |
| 650 | + it "should return a single key" do | |
| 651 | + project.deploy_keys << key | |
| 652 | + project.save | |
| 653 | + get api("/projects/#{project.id}/keys/#{key.id}", user) | |
| 654 | + response.status.should == 200 | |
| 655 | + json_response['title'].should == key.title | |
| 656 | + end | |
| 657 | + | |
| 658 | + it "should return 404 Not Found with invalid ID" do | |
| 659 | + get api("/projects/#{project.id}/keys/404", user) | |
| 660 | + response.status.should == 404 | |
| 661 | + end | |
| 662 | + end | |
| 663 | + | |
| 664 | + describe "POST /projects/:id/keys" do | |
| 665 | + it "should not create an invalid ssh key" do | |
| 666 | + post api("/projects/#{project.id}/keys", user), { title: "invalid key" } | |
| 667 | + response.status.should == 404 | |
| 668 | + end | |
| 669 | + | |
| 670 | + it "should create new ssh key" do | |
| 671 | + key_attrs = attributes_for :key | |
| 672 | + expect { | |
| 673 | + post api("/projects/#{project.id}/keys", user), key_attrs | |
| 674 | + }.to change{ project.deploy_keys.count }.by(1) | |
| 675 | + end | |
| 676 | + end | |
| 677 | + | |
| 678 | + describe "DELETE /projects/:id/keys/:key_id" do | |
| 679 | + it "should delete existing key" do | |
| 680 | + project.deploy_keys << key | |
| 681 | + project.save | |
| 682 | + expect { | |
| 683 | + delete api("/projects/#{project.id}/keys/#{key.id}", user) | |
| 684 | + }.to change{ project.deploy_keys.count }.by(-1) | |
| 685 | + end | |
| 686 | + | |
| 687 | + it "should return 404 Not Found with invalid ID" do | |
| 688 | + delete api("/projects/#{project.id}/keys/404", user) | |
| 689 | + response.status.should == 404 | |
| 690 | + end | |
| 691 | + end | |
| 594 | 692 | end | ... | ... |
| ... | ... | @@ -0,0 +1,69 @@ |
| 1 | +require 'spec_helper' | |
| 2 | + | |
| 3 | +describe Gitlab::API do | |
| 4 | + include ApiHelpers | |
| 5 | + | |
| 6 | + let(:user) { create(:user) } | |
| 7 | + let(:admin) { create(:admin) } | |
| 8 | + let!(:hook) { create(:system_hook, url: "http://example.com") } | |
| 9 | + | |
| 10 | + before { stub_request(:post, hook.url) } | |
| 11 | + | |
| 12 | + describe "GET /hooks" do | |
| 13 | + context "when not an admin" do | |
| 14 | + it "should return forbidden error" do | |
| 15 | + get api("/hooks", user) | |
| 16 | + response.status.should == 403 | |
| 17 | + end | |
| 18 | + end | |
| 19 | + | |
| 20 | + context "when authenticated as admin" do | |
| 21 | + it "should return an array of hooks" do | |
| 22 | + get api("/hooks", admin) | |
| 23 | + response.status.should == 200 | |
| 24 | + json_response.should be_an Array | |
| 25 | + json_response.first['url'].should == hook.url | |
| 26 | + end | |
| 27 | + end | |
| 28 | + end | |
| 29 | + | |
| 30 | + describe "POST /hooks" do | |
| 31 | + it "should create new hook" do | |
| 32 | + expect { | |
| 33 | + post api("/hooks", admin), url: 'http://example.com' | |
| 34 | + }.to change { SystemHook.count }.by(1) | |
| 35 | + end | |
| 36 | + | |
| 37 | + it "should respond with 404 on failure" do | |
| 38 | + post api("/hooks", admin) | |
| 39 | + response.status.should == 404 | |
| 40 | + end | |
| 41 | + | |
| 42 | + it "should not create new hook without url" do | |
| 43 | + expect { | |
| 44 | + post api("/hooks", admin) | |
| 45 | + }.to_not change { SystemHook.count } | |
| 46 | + end | |
| 47 | + end | |
| 48 | + | |
| 49 | + describe "GET /hooks/:id" do | |
| 50 | + it "should return hook by id" do | |
| 51 | + get api("/hooks/#{hook.id}", admin) | |
| 52 | + response.status.should == 200 | |
| 53 | + json_response['event_name'].should == 'project_create' | |
| 54 | + end | |
| 55 | + | |
| 56 | + it "should return 404 on failure" do | |
| 57 | + get api("/hooks/404", admin) | |
| 58 | + response.status.should == 404 | |
| 59 | + end | |
| 60 | + end | |
| 61 | + | |
| 62 | + describe "DELETE /hooks/:id" do | |
| 63 | + it "should delete a hook" do | |
| 64 | + expect { | |
| 65 | + delete api("/hooks/#{hook.id}", admin) | |
| 66 | + }.to change { SystemHook.count }.by(-1) | |
| 67 | + end | |
| 68 | + end | |
| 69 | +end | |
| 0 | 70 | \ No newline at end of file | ... | ... |
spec/requests/api/users_spec.rb
| ... | ... | @@ -167,6 +167,22 @@ describe Gitlab::API do |
| 167 | 167 | end |
| 168 | 168 | end |
| 169 | 169 | |
| 170 | + describe "POST /users/:id/keys" do | |
| 171 | + before { admin } | |
| 172 | + | |
| 173 | + it "should not create invalid ssh key" do | |
| 174 | + post api("/users/#{user.id}/keys", admin), { title: "invalid key" } | |
| 175 | + response.status.should == 404 | |
| 176 | + end | |
| 177 | + | |
| 178 | + it "should create ssh key" do | |
| 179 | + key_attrs = attributes_for :key | |
| 180 | + expect { | |
| 181 | + post api("/users/#{user.id}/keys", admin), key_attrs | |
| 182 | + }.to change{ user.keys.count }.by(1) | |
| 183 | + end | |
| 184 | + end | |
| 185 | + | |
| 170 | 186 | describe "DELETE /users/:id" do |
| 171 | 187 | before { admin } |
| 172 | 188 | ... | ... |
spec/routing/project_routing_spec.rb
| ... | ... | @@ -56,7 +56,6 @@ end |
| 56 | 56 | # projects POST /projects(.:format) projects#create |
| 57 | 57 | # new_project GET /projects/new(.:format) projects#new |
| 58 | 58 | # wall_project GET /:id/wall(.:format) projects#wall |
| 59 | -# graph_project GET /:id/graph(.:format) projects#graph | |
| 60 | 59 | # files_project GET /:id/files(.:format) projects#files |
| 61 | 60 | # edit_project GET /:id/edit(.:format) projects#edit |
| 62 | 61 | # project GET /:id(.:format) projects#show |
| ... | ... | @@ -75,10 +74,6 @@ describe ProjectsController, "routing" do |
| 75 | 74 | get("/gitlabhq/wall").should route_to('projects#wall', id: 'gitlabhq') |
| 76 | 75 | end |
| 77 | 76 | |
| 78 | - it "to #graph" do | |
| 79 | - get("/gitlabhq/graph/master").should route_to('graph#show', project_id: 'gitlabhq', id: 'master') | |
| 80 | - end | |
| 81 | - | |
| 82 | 77 | it "to #files" do |
| 83 | 78 | get("/gitlabhq/files").should route_to('projects#files', id: 'gitlabhq') |
| 84 | 79 | end |
| ... | ... | @@ -202,6 +197,7 @@ describe RefsController, "routing" do |
| 202 | 197 | it "to #logs_tree" do |
| 203 | 198 | get("/gitlabhq/refs/stable/logs_tree").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'stable') |
| 204 | 199 | get("/gitlabhq/refs/stable/logs_tree/foo/bar/baz").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz') |
| 200 | + get("/gitlab/gitlabhq/refs/stable/logs_tree/files.scss").should route_to('refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'files.scss') | |
| 205 | 201 | end |
| 206 | 202 | end |
| 207 | 203 | |
| ... | ... | @@ -301,6 +297,10 @@ describe CommitsController, "routing" do |
| 301 | 297 | let(:actions) { [:show] } |
| 302 | 298 | let(:controller) { 'commits' } |
| 303 | 299 | end |
| 300 | + | |
| 301 | + it "to #show" do | |
| 302 | + get("/gitlab/gitlabhq/commits/master.atom").should route_to('commits#show', project_id: 'gitlab/gitlabhq', id: "master", format: "atom") | |
| 303 | + end | |
| 304 | 304 | end |
| 305 | 305 | |
| 306 | 306 | # project_team_members GET /:project_id/team_members(.:format) team_members#index |
| ... | ... | @@ -385,6 +385,7 @@ end |
| 385 | 385 | describe BlameController, "routing" do |
| 386 | 386 | it "to #show" do |
| 387 | 387 | get("/gitlabhq/blame/master/app/models/project.rb").should route_to('blame#show', project_id: 'gitlabhq', id: 'master/app/models/project.rb') |
| 388 | + get("/gitlab/gitlabhq/blame/master/files.scss").should route_to('blame#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss') | |
| 388 | 389 | end |
| 389 | 390 | end |
| 390 | 391 | |
| ... | ... | @@ -393,6 +394,7 @@ describe BlobController, "routing" do |
| 393 | 394 | it "to #show" do |
| 394 | 395 | get("/gitlabhq/blob/master/app/models/project.rb").should route_to('blob#show', project_id: 'gitlabhq', id: 'master/app/models/project.rb') |
| 395 | 396 | get("/gitlabhq/blob/master/app/models/compare.rb").should route_to('blob#show', project_id: 'gitlabhq', id: 'master/app/models/compare.rb') |
| 397 | + get("/gitlab/gitlabhq/blob/master/files.scss").should route_to('blob#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss') | |
| 396 | 398 | end |
| 397 | 399 | end |
| 398 | 400 | |
| ... | ... | @@ -400,6 +402,7 @@ end |
| 400 | 402 | describe TreeController, "routing" do |
| 401 | 403 | it "to #show" do |
| 402 | 404 | get("/gitlabhq/tree/master/app/models/project.rb").should route_to('tree#show', project_id: 'gitlabhq', id: 'master/app/models/project.rb') |
| 405 | + get("/gitlab/gitlabhq/tree/master/files.scss").should route_to('tree#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss') | |
| 403 | 406 | end |
| 404 | 407 | end |
| 405 | 408 | |
| ... | ... | @@ -420,3 +423,10 @@ describe CompareController, "routing" do |
| 420 | 423 | get("/gitlabhq/compare/issue/1234...stable").should route_to('compare#show', project_id: 'gitlabhq', from: 'issue/1234', to: 'stable') |
| 421 | 424 | end |
| 422 | 425 | end |
| 426 | + | |
| 427 | +describe GraphController, "routing" do | |
| 428 | + it "to #show" do | |
| 429 | + get("/gitlabhq/graph/master").should route_to('graph#show', project_id: 'gitlabhq', id: 'master') | |
| 430 | + get("/gitlabhq/graph/master.json").should route_to('graph#show', project_id: 'gitlabhq', id: 'master', format: "json") | |
| 431 | + end | |
| 432 | +end | ... | ... |
spec/support/stubbed_repository.rb