Merge branch 'master' into fixes/api, code clean up and tests fixed

Conflicts:
	doc/api/projects.md
	spec/requests/api/projects_spec.rb
5-0-stable
Sebastian Ziebell 2013-03-07 14:51:56 +01:00
commit 3374027e3a
49 changed files with 820 additions and 163 deletions

4
Capfile.example Normal file
View File

@ -0,0 +1,4 @@
load 'deploy'
load 'deploy/assets'
require 'bundler/capistrano'
load 'config/deploy'

View File

@ -1,4 +1,4 @@
source "http://rubygems.org"
source "https://rubygems.org"
def darwin_only(require_as)
RUBY_PLATFORM.include?('darwin') && require_as
@ -103,6 +103,9 @@ gem 'settingslogic'
gem "foreman"
gem "git"
# Cache
gem "redis-rails"
group :assets do
gem "sass-rails", "~> 3.2.5"
gem "coffee-rails", "~> 3.2.2"

View File

@ -13,7 +13,7 @@ GIT
raphael-rails (2.1.0)
GEM
remote: http://rubygems.org/
remote: https://rubygems.org/
specs:
actionmailer (3.2.12)
actionpack (= 3.2.12)
@ -329,8 +329,24 @@ GEM
json (~> 1.4)
redcarpet (2.2.2)
redis (3.0.2)
redis-actionpack (3.2.3)
actionpack (~> 3.2.3)
redis-rack (~> 1.4.0)
redis-store (~> 1.1.0)
redis-activesupport (3.2.3)
activesupport (~> 3.2.3)
redis-store (~> 1.1.0)
redis-namespace (1.2.1)
redis (~> 3.0.0)
redis-rack (1.4.2)
rack (~> 1.4.1)
redis-store (~> 1.1.0)
redis-rails (3.2.3)
redis-actionpack (~> 3.2.3)
redis-activesupport (~> 3.2.3)
redis-store (~> 1.1.0)
redis-store (1.1.3)
redis (>= 2.2.0)
request_store (1.0.5)
rspec (2.12.0)
rspec-core (~> 2.12.0)
@ -504,6 +520,7 @@ DEPENDENCIES
rb-fsevent
rb-inotify
redcarpet (~> 2.2.2)
redis-rails
rspec-rails (= 2.12.2)
sass-rails (~> 3.2.5)
sdoc

View File

@ -5,14 +5,14 @@
### GitLab allows you to
* keep your code secure on your own server
* manage repositories, users and access permissions
* communicate though issues, line-comments and wiki's
* perform code reviews with merge requests
* communicate through issues, line-comments and wiki pages
* perform code review with merge requests
### GitLab is
* powered by Ruby on Rails
* completely free and open source (MIT license)
* used by 10.000 organization to keep their code secure
* used by 10.000 organizations to keep their code secure
### Code status
@ -34,28 +34,35 @@
### Requirements
* Ubuntu/Debian*
* Ubuntu/Debian**
* ruby 1.9.3+
* MySQL
* git
* gitlab-shell
* redis
* More details are in the [requirements doc](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/requirements.md)
** More details are in the [requirements doc](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/requirements.md)
### Installation
You can either follow the "ordinary" Installation guide to install it on a machine or use the Vagrant virtual machine. The Installation guide is recommended to set up a production server. The Vargrant virtual machine is recommended for development since it makes it much easier to set up all the dependencies for integration testing.
#### For production
* [Installation guide for latest stable release](https://github.com/gitlabhq/gitlabhq/blob/4-2-stable/doc/install/installation.md)
Follow the installation guide for production server.
* [Installation guide for the current master branch](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md)
* [Installation guide for latest stable release (4.2)](https://github.com/gitlabhq/gitlabhq/blob/4-2-stable/doc/install/installation.md) - **Recommended**
* [Installation guide for the current master branch (5.0)](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md)
#### For development
If you want to contribute, please first read our [Contributing Guidelines](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) and then we suggest you to use the Vagrant virtual machine project to get an environment working sandboxed and with all dependencies.
* [Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm)
### Starting
1. The Installation guide contains instructions to download an init script and run that on boot. With the init script you can also start GitLab with:
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
sudo service gitlab start
@ -63,18 +70,18 @@ You can either follow the "ordinary" Installation guide to install it on a machi
sudo /etc/init.d/gitlab restart
2. Start it with [Foreman](https://github.com/ddollar/foreman) in development model
2. Start it with [Foreman](https://github.com/ddollar/foreman) in development mode
bundle exec foreman start -p 3000
3. Start it manually in development mode
or start it manually
bundle exec rails s
bundle exec rake sidekiq:start
### Running the tests
* Seed the database with
* Seed the database
bundle exec rake db:setup RAILS_ENV=test
bundle exec rake db:seed_fu RAILS_ENV=test

View File

@ -11,12 +11,7 @@ $ ->
# Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
$("#tree-slider .tree-item").live 'click', (e) ->
$('.tree-item-file-name a', this).trigger('click') if (e.target.nodeName != "A")
# Show/Hide the loading spinner
$('#tree-slider .tree-item-file-name a, .breadcrumb a, .project-refs-form').live
"ajax:beforeSend": -> $('.tree_progress').addClass("loading")
"ajax:complete": -> $('.tree_progress').removeClass("loading")
# Maintain forward/back history while browsing the file tree
((window) ->
History = window.History
@ -33,7 +28,12 @@ $ ->
History.Adapter.bind window, 'statechange', ->
state = History.getState()
window.ajaxGet(state.url)
$.ajax({
url: state.url,
dataType: 'script',
beforeSend: -> $('.tree_progress').addClass("loading"),
complete: -> $('.tree_progress').removeClass("loading")
})
)(window)
# See if there are lines selected

View File

@ -34,13 +34,6 @@
padding: 15px;
word-wrap: break-word;
pre {
background: none !important;
margin: 0;
border: none;
padding: 0;
}
.clearfix {
margin: 0;
}

View File

@ -2,6 +2,7 @@
.cgray { color:gray }
.cred { color:#D12F19 }
.cgreen { color:#4a2 }
.cblue { color:#29A }
.cblack { color:#111 }
.cdark { color:#444 }
.cwhite { color:#fff!important }

View File

@ -120,3 +120,16 @@ ul.nav.nav-projects-tabs {
.team_member_row form {
margin: 0px;
}
.public-projects {
li {
margin-top: 8px;
margin-bottom: 5px;
border-bottom: 1px solid #eee;
.description {
margin-left: 22px;
color: #aaa;
}
}
}

View File

@ -91,7 +91,7 @@ class MergeRequest < ActiveRecord::Base
def validate_branches
if target_branch == source_branch
errors.add :base, "You can not use same branch for source and target branches"
errors.add :branch_conflict, "You can not use same branch for source and target branches"
end
end

View File

@ -1,4 +1,6 @@
class Repository
include Gitlab::Popen
# Repository directory name with namespace direcotry
# Examples:
# gitlab/gitolite
@ -147,4 +149,21 @@ class Repository
file_path
end
# Return repo size in megabytes
# Cached in redis
def size
Rails.cache.fetch(cache_key(:size)) do
size = popen('du -s', path_to_repo).first.strip.to_i
(size.to_f / 1024).round(2)
end
end
def expire_cache
Rails.cache.delete(cache_key(:size))
end
def cache_key(type)
"#{type}:#{path_with_namespace}"
end
end

View File

@ -23,6 +23,7 @@ class GitPushService
project.ensure_satellite_exists
project.discover_default_branch
project.repository.expire_cache
if push_to_branch?(ref, oldrev)
project.update_merge_requests(oldrev, newrev, ref, @user)

View File

@ -1,6 +1,6 @@
= form_tag(user_omniauth_callback_path(:ldap), :class => "login-box", :id => 'new_ldap_user' ) do
= image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo"
= text_field_tag :username, nil, {:class => "text top", :placeholder => "LDAP Login"}
= text_field_tag :username, nil, {:class => "text top", :placeholder => "LDAP Login", :autofocus => "autofocus"}
= password_field_tag :password, nil, {:class => "text bottom", :placeholder => "Password"}
%br/
= submit_tag "LDAP Sign in", :class => "btn-primary btn"

View File

@ -1,15 +1,16 @@
- if event.proper?
%div.event-item
%span.cgray.pull-right
#{time_ago_in_words(event.created_at)} ago.
= cache event do
%div.event-item
%span.cgray.pull-right
#{time_ago_in_words(event.created_at)} ago.
= image_tag gravatar_icon(event.author_email), class: "avatar s24"
= image_tag gravatar_icon(event.author_email), class: "avatar s24"
- if event.push?
= render "events/event/push", event: event
.clearfix
- elsif event.note?
= render "events/event/note", event: event
- else
= render "events/event/common", event: event
- if event.push?
= render "events/event/push", event: event
.clearfix
- elsif event.note?
= render "events/event/note", event: event
- else
= render "events/event/common", event: event

View File

@ -21,6 +21,8 @@
= link_to "Milestones", "#milestones", 'data-toggle' => 'tab'
%li
= link_to "Notes", "#notes", 'data-toggle' => 'tab'
%li
= link_to "System Hooks", "#system_hooks", 'data-toggle' => 'tab'
.tab-content
.tab-pane.active#README
@ -103,3 +105,12 @@
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "notes.md"))
.tab-pane#system_hooks
.file_holder
.file_title
%i.icon-file
System Hooks
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "system_hooks.md"))

View File

@ -22,7 +22,7 @@
= mail_to Gitlab.config.gitlab.support_email, "support contact"
%li
Use the
= link_to "search bar", '#', onclick: "$("#search").focus();"
= link_to "search bar", '#', onclick: "$('#search').focus();"
on the top of this page
%li
Ask in our

View File

@ -10,7 +10,7 @@
= link_to root_path, class: "home" do
%h1 GITLAB
%span.separator
%h1.project_name Public Projects
%h1.project_name Public Projects
.container
.content
.prepend-top-20

View File

@ -9,11 +9,19 @@
Project name is
.input
= f.text_field :name, placeholder: "Example Project", class: "xxlarge"
- unless @repository.heads.empty?
.clearfix
= f.label :default_branch, "Default Branch"
.input= f.select(:default_branch, @repository.heads.map(&:name), {}, style: "width:210px;")
.clearfix
= f.label :description do
Project description
%span.light (optional)
.input
= f.text_area :description, placeholder: "awesome project", class: "xxlarge", rows: 3, maxlength: 250
%fieldset.features
%legend Features:

View File

@ -1,8 +1,33 @@
= render "project_head"
= render 'clone_panel'
= render "events/event_last_push", event: @last_push
.content_list= render @events
.loading.hide
.row
.span9
.content_list= render @events
.loading.hide
.span3
.ui-box.white
.padded
%h3.page_title
= @project.name
- if @project.description.present?
%p.light= @project.description
%hr
%p
Access level:
- if @project.public
%span.cblue
%i.icon-share
Public
- else
%span.cgreen
%i.icon-lock
Private
%p Repo Size: #{@project.repository.size} MB
%p Created at: #{@project.created_at.stamp('Aug 22, 2013')}
%p Owner: #{link_to @project.owner_name, @project.owner}
:javascript
$(function(){ Pager.init(20); });

View File

@ -1,18 +1,20 @@
%h3.page_title
Projects
Projects (#{@projects.total_count})
%small with read-only access
%hr
%ul.unstyled
- @projects.each do |project|
%li.clearfix
%h5
%i.icon-share
= project.name_with_namespace
.pull-right
%pre.dark.tiny git clone #{project.http_url_to_repo}
.public-projects
%ul.unstyled
- @projects.each do |project|
%li.clearfix
%h5
%i.icon-share
= project.name_with_namespace
.pull-right
%pre.dark.tiny git clone #{project.http_url_to_repo}
%p.description
= project.description
- unless @projects.present?
%h3.nothing_here_message No public projects
- unless @projects.present?
%h3.nothing_here_message No public projects
= paginate @projects, theme: "admin"
= paginate @projects, theme: "admin"

View File

@ -11,9 +11,6 @@
- else
= link_to title, '#'
.clear
%div.tree_progress
%div#tree-content-holder.tree-content-holder
- if tree.is_blob?
= render "tree/blob", blob: tree
@ -40,6 +37,8 @@
- if tree.readme
= render "tree/readme", readme: tree.readme
%div.tree_progress
- unless tree.is_blob?
:javascript
// Load last commit log for each file in tree

72
config/deploy.rb.example Normal file
View File

@ -0,0 +1,72 @@
set :domain, 'set application domain here'
set :db_adapter, 'mysql' # or postgres
set :mount_point, '/'
set :application, 'gitlabhq'
set :user, 'git'
set :rails_env, 'production'
set :deploy_to, "/home/#{user}/apps/#{application}"
set :bundle_without, %w[development test] + (%w[mysql postgres] - [db_adapter])
set :asset_env, "RAILS_GROUPS=assets RAILS_RELATIVE_URL_ROOT=#{mount_point.sub /\/+\Z/, ''}"
set :use_sudo, false
default_run_options[:pty] = true
# Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none`
set :scm, :git
set :repository, "git@#{domain}:#{application}.git"
set :deploy_via, :remote_cache
# Alternatively, you can deploy via copy, if you don't have gitlab in git
#set :scm, :none
#set :repository, '.'
#set :deploy_via, :copy
server domain, :app, :web, :db, primary: true
namespace :foreman do
desc 'Export the Procfile to Ubuntu upstart scripts'
task :export, roles: :app do
foreman_export = "foreman export upstart /etc/init -f Procfile -a #{application} -u #{user} -l #{shared_path}/log/foreman"
run "cd #{release_path} && #{sudo} #{fetch :bundle_cmd, 'bundle'} exec #{foreman_export}"
end
desc 'Start the application services'
task :start, roles: :app do
run "#{sudo} service #{application} start"
end
desc 'Stop the application services'
task :stop, roles: :app do
run "#{sudo} service #{application} stop"
end
desc 'Restart the application services'
task :restart, roles: :app do
run "#{sudo} service #{application} restart"
end
end
namespace :deploy do
desc 'Start the application services'
task :start, roles: :app do
foreman.start
end
desc 'Stop the application services'
task :stop, roles: :app do
foreman.stop
end
desc 'Restart the application services'
task :restart, roles: :app do
foreman.restart
end
end
after 'deploy:cold' do
run "cd #{release_path} && #{rake} gitlab:setup force=yes RAILS_ENV=#{rails_env}"
deploy.restart
end
after 'deploy:update', 'foreman:export' # Export foreman scripts
#after 'deploy:update', 'foreman:restart' # Restart application scripts

View File

@ -40,7 +40,7 @@ Gitlab::Application.configure do
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
# Use a different cache store in production
config.cache_store = :memory_store
config.cache_store = :redis_store
# Enable serving of images, stylesheets, and JavaScripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com"

View File

@ -4,19 +4,19 @@ config_file = Rails.root.join('config', 'resque.yml')
resque_url = if File.exists?(config_file)
YAML.load_file(config_file)[Rails.env]
else
"localhost:6379"
"redis://localhost:6379"
end
Sidekiq.configure_server do |config|
config.redis = {
url: "redis://#{resque_url}",
url: resque_url,
namespace: 'resque:gitlab'
}
end
Sidekiq.configure_client do |config|
config.redis = {
url: "redis://#{resque_url}",
url: resque_url,
namespace: 'resque:gitlab'
}
end

View File

@ -1,3 +1,3 @@
development: localhost:6379
test: localhost:6379
production: redis.example.com:6379
development: redis://localhost:6379
test: redis://localhost:6379
production: redis://redis.example.com:6379

View File

@ -166,7 +166,7 @@ Gitlab::Application.routes.draw do
#
# Project Area
#
resources :projects, constraints: { id: /[a-zA-Z.0-9_\-\/]+/ }, except: [:new, :create, :index], path: "/" do
resources :projects, constraints: { id: /(?:[a-zA-Z.0-9_\-]+\/)?[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index], path: "/" do
member do
get "wall"
get "files"
@ -175,10 +175,10 @@ Gitlab::Application.routes.draw do
resources :blob, only: [:show], constraints: {id: /.+/}
resources :tree, only: [:show, :edit, :update], constraints: {id: /.+/}
resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
resources :commits, only: [:show], constraints: {id: /.+/}
resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
resources :compare, only: [:index, :create]
resources :blame, only: [:show], constraints: {id: /.+/}
resources :graph, only: [:show], constraints: {id: /.+/}
resources :graph, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
match "/compare/:from...:to" => "compare#show", as: "compare",
:via => [:get, :post], constraints: {from: /.+/, to: /.+/}

View File

@ -31,13 +31,10 @@ The API uses JSON to serialize data. You don't need to specify `.json` at the en
## Status codes
API requests return different status codes according to
The API is designed to provide status codes according to the context and how the request
is handled. For example if a `GET` request is successful a status code `200 Ok`
is returned. The API is designed to be RESTful.
The following list gives an overview of how the API functions are designed.
The API is designed to return different status codes according to context and action. In this way
if a request results in an error the caller is able to get insight into what went wrong, e.g.
status code `400 Bad Request` is returned if a required attribute is missing from the request.
The following list gives an overview of how the API functions generally behave.
API request types:
@ -58,7 +55,7 @@ Return values:
* `403 Forbidden` - The request is not allowed, e.g. the user is not allowed to delete a project
* `404 Not Found` - A resource could not be accessed, e.g. an ID for a resource could not be found
* `405 Method Not Allowed` - The request is not supported
* `409 Conflict` - A conflicting resource already exists, a project with same name already exists
* `409 Conflict` - A conflicting resource already exists, e.g. creating a project with a name that already exists
* `500 Server Error` - While handling the request something went wrong on the server side

View File

@ -44,3 +44,14 @@ Parameters:
+ `name` (required) - The name of the group
+ `path` (required) - The path of the group
## Transfer project to group
Transfer a project to the Group namespace. Available only for admin
```
POST /groups/:id/projects/:project_id
```
Parameters:
+ `id` (required) - The ID of a group
+ `project_id (required) - The ID of a project

View File

@ -115,11 +115,9 @@ Parameters:
+ `merge_requests_enabled` (optional) - enabled by default
+ `wiki_enabled` (optional) - enabled by default
**Project access levels**
## Project access levels
The project access levels are defined in the `user_project` class. Currently, 4
levels are recoginized:
The project access levels are defined in the `user_project.rb` class. Currently, these levels are recoginized:
```
GUEST = 10
@ -129,7 +127,30 @@ levels are recoginized:
```
## List project team members
### Create project for user
Creates a new project owned by user. Available only for admins.
```
POST /projects/user/:user_id
```
Parameters:
+ `user_id` (required) - user_id of owner
+ `name` (required) - new project name
+ `description` (optional) - short project description
+ `default_branch` (optional) - 'master' by default
+ `issues_enabled` (optional) - enabled by default
+ `wall_enabled` (optional) - enabled by default
+ `merge_requests_enabled` (optional) - enabled by default
+ `wiki_enabled` (optional) - enabled by default
## Team members
### List project team members
Get a list of project team members.
@ -140,14 +161,12 @@ GET /projects/:id/members
Parameters:
+ `id` (required) - The ID or NAME of a project
+ `query` - Query string
+ `query` (optional) - Query string to search for members
## Team members
### Get project team member
Get a project team member.
Gets a project team member.
```
GET /projects/:id/members/:user_id
@ -175,7 +194,7 @@ Parameters:
Adds a user to a project team. This is an idempotent method and can be called multiple times
with the same parameters. Adding team membership to a user that is already a member does not
affect the membership.
affect the existing membership.
```
POST /projects/:id/members
@ -190,7 +209,7 @@ Parameters:
### Edit project team member
Update project team member to specified access level.
Updates project team member to a specified access level.
```
PUT /projects/:id/members/:user_id
@ -398,81 +417,90 @@ Returns values:
+ `404 Not Found` if project with id or the branch with `ref_name` not found
## Snippets
### List snippets
## Deploy Keys
Lists the snippets of a project.
### List deploy keys
Get a list of a project's deploy keys.
```
GET /projects/:id/snippets
GET /projects/:id/keys
```
Parameters:
+ `id` (required) - The ID of the project
```json
[
{
"id": 1,
"title" : "Public key"
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
},
{
"id": 3,
"title" : "Another Public key"
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="
}
]
```
### List single snippet
Lists a single snippet of a project
### Single deploy key
Get a single key.
```
GET /projects/:id/snippets/:snippet_id
GET /projects/:id/keys/:key_id
```
Parameters:
+ `id` (required) - The ID of the project
+ `snippet_id` (required) - The ID of the snippet
+ `key_id` (required) - The ID of the deploy key
```json
{
"id": 1,
"title" : "Public key"
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="
}
```
### Create snippet
### Add deploy key
Creates a new project snippet.
Creates a new deploy key for a project.
```
POST /projects/:id/snippets
POST /projects/:id/keys
```
Parameters:
+ `id` (required) - The ID of the project
+ `title` (required) - The title of the new snippet
+ `file_name` (required) - The file name of the snippet
+ `code` (required) - The content of the snippet
+ `lifetime` (optional) - The expiration date of a snippet
+ `title` (required) - New deploy key's title
+ `key` (required) - New deploy key
### Update snippet
### Delete deploy key
Updates an existing project snippet.
Delete a deploy key from a project
```
PUT /projects/:id/snippets/:snippet_id
DELETE /projects/:id/keys/:key_id
```
Parameters:
+ `id` (required) - The ID of the project
+ `snippet_id` (required) - The id of the project snippet
+ `title` (optional) - The new title of the project snippet
+ `file_name` (optional) - The new file name of the project snippet
+ `lifetime` (optional) - The new expiration date of the snippet
+ `code` (optional) - The content of the snippet
## Delete snippet
Deletes a project snippet. This is an idempotent function call and returns `200 Ok`
even if the snippet with the id is not available.
```
DELETE /projects/:id/snippets/:snippet_id
```
Paramaters:
+ `id` (required) - The ID of the project
+ `snippet_id` (required) - The ID of the snippet
+ `key_id` (required) - The ID of the deploy key

View File

@ -46,7 +46,7 @@ Parameters:
## Create new snippet
Creates a new project snippet.
Creates a new project snippet. The user must have permission to create new snippets.
```
POST /projects/:id/snippets
@ -61,9 +61,9 @@ Parameters:
+ `code` (required) - The content of a snippet
## Edit snippet
## Update snippet
Updates an existing project snippet.
Updates an existing project snippet. The user must have permission to change an existing snippet.
```
PUT /projects/:id/snippets/:snippet_id
@ -96,7 +96,7 @@ Parameters:
## Snippet content
Get a raw project snippet.
Returns the raw project snippet as plain text.
```
GET /projects/:id/snippets/:snippet_id/raw

47
doc/api/system_hooks.md Normal file
View File

@ -0,0 +1,47 @@
All methods require admin authorization.
## List system hooks
Get list of system hooks
```
GET /hooks
```
Will return hooks with status `200 OK` on success, or `404 Not found` on fail.
## Add new system hook hook
```
POST /hooks
```
Parameters:
+ `url` (required) - The hook URL
Will return status `201 Created` on success, or `404 Not found` on fail.
## Test system hook
```
GET /hooks/:id
```
Parameters:
+ `id` (required) - The ID of hook
Will return hook with status `200 OK` on success, or `404 Not found` on fail.
## Delete system hook
```
DELETE /hooks/:id
```
Parameters:
+ `id` (required) - The ID of hook
Will return status `200 OK` on success, or `404 Not found` on fail.

View File

@ -235,6 +235,23 @@ Parameters:
+ `key` (required) - new SSH key
## Add SSH key for user
Create new key owned by specified user. Available only for admin
```
POST /users/:id/keys
```
Parameters:
+ `id` (required) - id of specified user
+ `title` (required) - new SSH Key's title
+ `key` (required) - new SSH key
Will return created key with status `201 Created` on success, or `404 Not
found` on fail.
## Delete SSH key
Deletes key owned by currently authenticated user. This is an idempotent function and calling it on a key that is already

View File

@ -288,7 +288,7 @@ a different host, you can configure its connection string via the
`config/resque.yml` file.
# example
production: redis.example.tld:6379
production: redis://redis.example.tld:6379
## Custom SSH Connection

View File

@ -27,6 +27,7 @@ class ProjectNetworkGraph < Spinach::FeatureSteps
And 'I switch ref to "stable"' do
page.select 'stable', :from => 'ref'
sleep 2
end
And 'page should select "stable" in select box' do
@ -44,6 +45,7 @@ class ProjectNetworkGraph < Spinach::FeatureSteps
fill_in 'q', :with => '98d6492'
find('button').click
end
sleep 2
end
And 'page should have "v2.1.0" on graph' do

View File

@ -33,5 +33,6 @@ module Gitlab
mount MergeRequests
mount Notes
mount Internal
mount SystemHooks
end
end

View File

@ -56,6 +56,24 @@ module Gitlab
not_found!
end
end
# Transfer a project to the Group namespace
#
# Parameters:
# id - group id
# project_id - project id
# Example Request:
# POST /groups/:id/projects/:project_id
post ":id/projects/:project_id" do
authenticated_as_admin!
@group = Group.find(params[:id])
project = Project.find(params[:project_id])
if project.transfer(@group)
present @group
else
not_found!
end
end
end
end
end

View File

@ -8,6 +8,8 @@ module Gitlab
def handle_merge_request_errors!(errors)
if errors[:project_access].any?
error!(errors[:project_access], 422)
elsif errors[:branch_conflict].any?
error!(errors[:branch_conflict], 422)
end
not_found!
end

View File

@ -64,6 +64,38 @@ module Gitlab
end
end
# Create new project for a specified user. Only available to admin users.
#
# Parameters:
# user_id (required) - The ID of a user
# name (required) - name for new project
# description (optional) - short project description
# default_branch (optional) - 'master' by default
# issues_enabled (optional) - enabled by default
# wall_enabled (optional) - enabled by default
# merge_requests_enabled (optional) - enabled by default
# wiki_enabled (optional) - enabled by default
# Example Request
# POST /projects/user/:user_id
post "user/:user_id" do
authenticated_as_admin!
user = User.find(params[:user_id])
attrs = attributes_for_keys [:name,
:description,
:default_branch,
:issues_enabled,
:wall_enabled,
:merge_requests_enabled,
:wiki_enabled]
@project = ::Projects::CreateContext.new(user, attrs).execute
if @project.saved?
present @project, with: Entities::Project
else
not_found!
end
end
# Get a project team members
#
# Parameters:
@ -471,6 +503,49 @@ module Gitlab
present tree.data
end
# Get a specific project's keys
#
# Example Request:
# GET /projects/:id/keys
get ":id/keys" do
present user_project.deploy_keys, with: Entities::SSHKey
end
# Get single key owned by currently authenticated user
#
# Example Request:
# GET /projects/:id/keys/:id
get ":id/keys/:key_id" do
key = user_project.deploy_keys.find params[:key_id]
present key, with: Entities::SSHKey
end
# Add new ssh key to currently authenticated user
#
# Parameters:
# key (required) - New SSH Key
# title (required) - New SSH Key's title
# Example Request:
# POST /projects/:id/keys
post ":id/keys" do
attrs = attributes_for_keys [:title, :key]
key = user_project.deploy_keys.new attrs
if key.save
present key, with: Entities::SSHKey
else
not_found!
end
end
# Delete existed ssh key of currently authenticated user
#
# Example Request:
# DELETE /projects/:id/keys/:id
delete ":id/keys/:key_id" do
key = user_project.deploy_keys.find params[:key_id]
key.delete
end
end
end
end

60
lib/api/system_hooks.rb Normal file
View File

@ -0,0 +1,60 @@
module Gitlab
# Hooks API
class SystemHooks < Grape::API
before { authenticated_as_admin! }
resource :hooks do
# Get the list of system hooks
#
# Example Request:
# GET /hooks
get do
@hooks = SystemHook.all
present @hooks, with: Entities::Hook
end
# Create new system hook
#
# Parameters:
# url (required) - url for system hook
# Example Request
# POST /hooks
post do
attrs = attributes_for_keys [:url]
@hook = SystemHook.new attrs
if @hook.save
present @hook, with: Entities::Hook
else
not_found!
end
end
# Test a hook
#
# Example Request
# GET /hooks/:id
get ":id" do
@hook = SystemHook.find(params[:id])
data = {
event_name: "project_create",
name: "Ruby",
path: "ruby",
project_id: 1,
owner_name: "Someone",
owner_email: "example@gitlabhq.com"
}
@hook.execute(data)
data
end
# Delete a hook
#
# Example Request:
# DELETE /hooks/:id
delete ":id" do
@hook = SystemHook.find(params[:id])
@hook.destroy
end
end
end
end

View File

@ -81,6 +81,26 @@ module Gitlab
end
end
# Add ssh key to a specified user. Only available to admin users.
#
# Parameters:
# id (required) - The ID of a user
# key (required) - New SSH Key
# title (required) - New SSH Key's title
# Example Request:
# POST /users/:id/keys
post ":id/keys" do
authenticated_as_admin!
user = User.find(params[:id])
attrs = attributes_for_keys [:title, :key]
key = user.keys.new attrs
if key.save
present key, with: Entities::SSHKey
else
not_found!
end
end
# Delete user. Available only for admin
#
# Example Request:

View File

@ -105,12 +105,6 @@ module ExtractsPath
# Automatically renders `not_found!` if a valid tree path could not be
# resolved (e.g., when a user inserts an invalid path or ref).
def assign_ref_vars
# Handle formats embedded in the id
if params[:id].ends_with?('.atom')
params[:id].gsub!(/\.atom$/, '')
request.format = :atom
end
path = CGI::unescape(request.fullpath.dup)
@ref, @path = extract_ref(path)

View File

@ -7,10 +7,12 @@ namespace :gitlab do
def setup_db
warn_user_is_not_gitlab
puts "This will create the necessary database tables and seed the database."
puts "You will lose any previous data stored in the database."
ask_to_continue
puts ""
unless ENV['force'] == 'yes'
puts "This will create the necessary database tables and seed the database."
puts "You will lose any previous data stored in the database."
ask_to_continue
puts ""
end
Rake::Task["db:setup"].invoke
Rake::Task["db:seed_fu"].invoke

View File

@ -13,7 +13,7 @@ describe CommitsController do
describe "GET show" do
context "as atom feed" do
it "should render as atom" do
get :show, project_id: project.path, id: "master.atom"
get :show, project_id: project.path, id: "master", format: "atom"
response.should be_success
response.content_type.should == 'application/atom+xml'
end

View File

@ -100,4 +100,27 @@ describe Gitlab::API do
end
end
end
describe "POST /groups/:id/projects/:project_id" do
let(:project) { create(:project) }
before(:each) do
project.stub!(:transfer).and_return(true)
Project.stub(:find).and_return(project)
end
context "when authenticated as user" do
it "should not transfer project to group" do
post api("/groups/#{group1.id}/projects/#{project.id}", user2)
response.status.should == 403
end
end
context "when authenticated as admin" do
it "should transfer project to group" do
project.should_receive(:transfer)
post api("/groups/#{group1.id}/projects/#{project.id}", admin)
end
end
end
end

View File

@ -105,13 +105,6 @@ describe Gitlab::API do
response.status.should == 404
end
end
context "when notable is invalid" do
it "should return a 404 error" do
get api("/projects/#{project.id}/unknown/#{snippet.id}/notes", user)
response.status.should == 404
end
end
end
describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do
@ -180,12 +173,5 @@ describe Gitlab::API do
response.status.should == 401
end
end
context "when noteable is invalid" do
it "should return a 404 error" do
post api("/projects/#{project.id}/invalid/#{snippet.id}/notes", user)
response.status.should == 404
end
end
end
end

View File

@ -6,11 +6,14 @@ describe Gitlab::API do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
let!(:project) { create(:project, namespace: user.namespace ) }
let!(:hook) { create(:project_hook, project: project, url: "http://example.com") }
let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') }
let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) }
let(:key) { create(:key, project: project) }
before { project.team << [user, :reporter] }
describe "GET /projects" do
@ -103,6 +106,46 @@ describe Gitlab::API do
end
end
describe "POST /projects/user/:id" do
before { admin }
it "should create new project without path" do
expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1)
end
it "should not create new project without name" do
expect { post api("/projects/user/#{user.id}", admin) }.to_not change {Project.count}
end
it "should respond with 201 on success" do
post api("/projects/user/#{user.id}", admin), name: 'foo'
response.status.should == 201
end
it "should respond with 404 on failure" do
post api("/projects/user/#{user.id}", admin)
response.status.should == 404
end
it "should assign attributes to project" do
project = attributes_for(:project, {
description: Faker::Lorem.sentence,
default_branch: 'stable',
issues_enabled: false,
wall_enabled: false,
merge_requests_enabled: false,
wiki_enabled: false
})
post api("/projects/user/#{user.id}", admin), project
project.each_pair do |k,v|
next if k == :path
json_response[k.to_s].should == v
end
end
end
describe "GET /projects/:id" do
it "should return a project by id" do
get api("/projects/#{project.id}", user)
@ -591,4 +634,59 @@ describe Gitlab::API do
response.status.should == 400
end
end
describe "GET /projects/:id/keys" do
it "should return array of ssh keys" do
project.deploy_keys << key
project.save
get api("/projects/#{project.id}/keys", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['title'].should == key.title
end
end
describe "GET /projects/:id/keys/:key_id" do
it "should return a single key" do
project.deploy_keys << key
project.save
get api("/projects/#{project.id}/keys/#{key.id}", user)
response.status.should == 200
json_response['title'].should == key.title
end
it "should return 404 Not Found with invalid ID" do
get api("/projects/#{project.id}/keys/404", user)
response.status.should == 404
end
end
describe "POST /projects/:id/keys" do
it "should not create an invalid ssh key" do
post api("/projects/#{project.id}/keys", user), { title: "invalid key" }
response.status.should == 404
end
it "should create new ssh key" do
key_attrs = attributes_for :key
expect {
post api("/projects/#{project.id}/keys", user), key_attrs
}.to change{ project.deploy_keys.count }.by(1)
end
end
describe "DELETE /projects/:id/keys/:key_id" do
it "should delete existing key" do
project.deploy_keys << key
project.save
expect {
delete api("/projects/#{project.id}/keys/#{key.id}", user)
}.to change{ project.deploy_keys.count }.by(-1)
end
it "should return 404 Not Found with invalid ID" do
delete api("/projects/#{project.id}/keys/404", user)
response.status.should == 404
end
end
end

View File

@ -0,0 +1,69 @@
require 'spec_helper'
describe Gitlab::API do
include ApiHelpers
let(:user) { create(:user) }
let(:admin) { create(:admin) }
let!(:hook) { create(:system_hook, url: "http://example.com") }
before { stub_request(:post, hook.url) }
describe "GET /hooks" do
context "when not an admin" do
it "should return forbidden error" do
get api("/hooks", user)
response.status.should == 403
end
end
context "when authenticated as admin" do
it "should return an array of hooks" do
get api("/hooks", admin)
response.status.should == 200
json_response.should be_an Array
json_response.first['url'].should == hook.url
end
end
end
describe "POST /hooks" do
it "should create new hook" do
expect {
post api("/hooks", admin), url: 'http://example.com'
}.to change { SystemHook.count }.by(1)
end
it "should respond with 404 on failure" do
post api("/hooks", admin)
response.status.should == 404
end
it "should not create new hook without url" do
expect {
post api("/hooks", admin)
}.to_not change { SystemHook.count }
end
end
describe "GET /hooks/:id" do
it "should return hook by id" do
get api("/hooks/#{hook.id}", admin)
response.status.should == 200
json_response['event_name'].should == 'project_create'
end
it "should return 404 on failure" do
get api("/hooks/404", admin)
response.status.should == 404
end
end
describe "DELETE /hooks/:id" do
it "should delete a hook" do
expect {
delete api("/hooks/#{hook.id}", admin)
}.to change { SystemHook.count }.by(-1)
end
end
end

View File

@ -167,6 +167,22 @@ describe Gitlab::API do
end
end
describe "POST /users/:id/keys" do
before { admin }
it "should not create invalid ssh key" do
post api("/users/#{user.id}/keys", admin), { title: "invalid key" }
response.status.should == 404
end
it "should create ssh key" do
key_attrs = attributes_for :key
expect {
post api("/users/#{user.id}/keys", admin), key_attrs
}.to change{ user.keys.count }.by(1)
end
end
describe "DELETE /users/:id" do
before { admin }

View File

@ -56,7 +56,6 @@ end
# projects POST /projects(.:format) projects#create
# new_project GET /projects/new(.:format) projects#new
# wall_project GET /:id/wall(.:format) projects#wall
# graph_project GET /:id/graph(.:format) projects#graph
# files_project GET /:id/files(.:format) projects#files
# edit_project GET /:id/edit(.:format) projects#edit
# project GET /:id(.:format) projects#show
@ -75,10 +74,6 @@ describe ProjectsController, "routing" do
get("/gitlabhq/wall").should route_to('projects#wall', id: 'gitlabhq')
end
it "to #graph" do
get("/gitlabhq/graph/master").should route_to('graph#show', project_id: 'gitlabhq', id: 'master')
end
it "to #files" do
get("/gitlabhq/files").should route_to('projects#files', id: 'gitlabhq')
end
@ -202,6 +197,7 @@ describe RefsController, "routing" do
it "to #logs_tree" do
get("/gitlabhq/refs/stable/logs_tree").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'stable')
get("/gitlabhq/refs/stable/logs_tree/foo/bar/baz").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz')
get("/gitlab/gitlabhq/refs/stable/logs_tree/files.scss").should route_to('refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'files.scss')
end
end
@ -301,6 +297,10 @@ describe CommitsController, "routing" do
let(:actions) { [:show] }
let(:controller) { 'commits' }
end
it "to #show" do
get("/gitlab/gitlabhq/commits/master.atom").should route_to('commits#show', project_id: 'gitlab/gitlabhq', id: "master", format: "atom")
end
end
# project_team_members GET /:project_id/team_members(.:format) team_members#index
@ -385,6 +385,7 @@ end
describe BlameController, "routing" do
it "to #show" do
get("/gitlabhq/blame/master/app/models/project.rb").should route_to('blame#show', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
get("/gitlab/gitlabhq/blame/master/files.scss").should route_to('blame#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
end
end
@ -393,6 +394,7 @@ describe BlobController, "routing" do
it "to #show" do
get("/gitlabhq/blob/master/app/models/project.rb").should route_to('blob#show', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
get("/gitlabhq/blob/master/app/models/compare.rb").should route_to('blob#show', project_id: 'gitlabhq', id: 'master/app/models/compare.rb')
get("/gitlab/gitlabhq/blob/master/files.scss").should route_to('blob#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
end
end
@ -400,6 +402,7 @@ end
describe TreeController, "routing" do
it "to #show" do
get("/gitlabhq/tree/master/app/models/project.rb").should route_to('tree#show', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
get("/gitlab/gitlabhq/tree/master/files.scss").should route_to('tree#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
end
end
@ -420,3 +423,10 @@ describe CompareController, "routing" do
get("/gitlabhq/compare/issue/1234...stable").should route_to('compare#show', project_id: 'gitlabhq', from: 'issue/1234', to: 'stable')
end
end
describe GraphController, "routing" do
it "to #show" do
get("/gitlabhq/graph/master").should route_to('graph#show', project_id: 'gitlabhq', id: 'master')
get("/gitlabhq/graph/master.json").should route_to('graph#show', project_id: 'gitlabhq', id: 'master', format: "json")
end
end

View File

@ -43,6 +43,11 @@ class GitLabTestRepo < Repository
def repo
@repo ||= Grit::Repo.new(Rails.root.join('tmp', 'repositories', 'gitlabhq'))
end
# patch repo size (in mb)
def size
12.45
end
end
module Gitlab