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