Merge branch 'master' into fix_project_access_notification

This commit is contained in:
Alex Denisov 2012-09-26 16:14:47 +03:00
commit c09d233611
74 changed files with 1157 additions and 845 deletions

View file

@ -1,3 +1,6 @@
v 2.9.1
- Fixed resque custom config init
v 2.9.0
- fixed inline notes bugs
- refactored rspecs
@ -9,8 +12,10 @@ v 2.9.0
- scss refactoring. gitlab_bootstrap/ dir
- fix git push http body bigger than 112k problem
- list of labels page under issues tab
- API for milestones
- API for milestones, keys
- restyled buttons
- OAuth
- Comment order changed
v 2.8.1
- ability to disable gravatars

View file

@ -96,6 +96,7 @@ group :assets do
gem "therubyracer"
gem 'chosen-rails'
gem 'jquery-atwho-rails', '0.1.6'
gem "jquery-rails", "2.0.2"
gem "jquery-ui-rails", "0.5.0"
gem "modernizr", "2.5.3"

View file

@ -199,6 +199,7 @@ GEM
httpauth (0.1)
i18n (0.6.1)
journey (1.0.4)
jquery-atwho-rails (0.1.6)
jquery-rails (2.0.2)
railties (>= 3.2.0, < 5.0)
thor (~> 0.14)
@ -441,6 +442,7 @@ DEPENDENCIES
haml-rails
headless
httparty
jquery-atwho-rails (= 0.1.6)
jquery-rails (= 2.0.2)
jquery-ui-rails (= 0.5.0)
kaminari

View file

@ -4,6 +4,7 @@
guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
# Rails example

View file

@ -1 +1 @@
2.9.0pre
2.9.1

View file

@ -11,6 +11,7 @@
//= require jquery.endless-scroll
//= require jquery.highlight
//= require jquery.waitforimages
//= require jquery.atwho
//= require bootstrap
//= require modernizr
//= require chosen-jquery

View file

@ -4,6 +4,7 @@
* the top of the compiled file, but it's generally better to create a new file per style scope.
*= require jquery.ui.all
*= require jquery.ui.aristo
*= require jquery.atwho
*= require chosen
*= require_self
*= require main

View file

@ -185,36 +185,6 @@ span.update-author {
}
}
.event_label {
@extend .label;
background-color: #999;
&.pushed {
background-color: #4A97BD;
}
&.opened {
background-color: #469847;
}
&.closed {
background-color: #B94A48;
}
&.merged {
background-color: #2A2;
}
&.joined {
background-color: #1ca9dd;
}
&.left {
background-color: #888;
float:none;
}
}
form {
@extend .form-horizontal;
@ -355,41 +325,6 @@ p.time {
border:2px solid #ddd;
}
.event_feed {
min-height:40px;
border-bottom:1px solid #ddd;
.avatar {
width:32px;
}
.event_icon {
float:right;
margin-right:2px;
img {
width:20px;
}
}
ul {
margin-left:50px;
margin-bottom:5px;
.avatar {
width:24px;
}
}
padding: 15px 5px;
&:last-child { border:none }
.wll:hover { background:none }
.event_commits {
margin-top: 5px;
li.commit {
background: transparent;
padding:5px;
border:none;
}
}
}
.ico {
background: url("images.png") no-repeat -85px -77px;
@ -639,22 +574,6 @@ li.note {
background:#fff;
}
/**
* Push event widget
*
*/
.event_lp {
@extend .ui-box;
color:#777;
margin-bottom:20px;
padding:8px;
@include border-radius(4px);
min-height:22px;
.avatar {
width:24px;
}
}
.supp_diff_link,
.mr_show_all_commits {

View file

@ -143,6 +143,7 @@ $hover: #fdf5d9;
@import "sections/projects.scss";
@import "sections/merge_requests.scss";
@import "sections/graph.scss";
@import "sections/events.scss";
/**
* This scss file redefine chozen selectbox styles for

View file

@ -0,0 +1,118 @@
/**
* Events labels
*
*/
.event_label {
&.pushed {
padding:0 2px;
@extend .alert;
@extend .alert-info;
}
&.opened {
padding:0 2px;
@extend .alert;
@extend .alert-success;
}
&.closed {
padding:0 2px;
@extend .alert;
@extend .alert-error;
}
&.merged {
padding:0 2px;
@extend .alert;
@extend .alert-success;
}
&.left,
&.joined {
padding:0 2px;
@extend .alert;
}
}
/**
* Dashboard events feed
*
*/
.event-item {
min-height:40px;
border-bottom:1px solid #eee;
.event-title {
color:#333;
font-weight: bold;
.author_name {
color:#333;
}
}
.event-body {
p {
color:#555;
}
.event-info {
color:#666;
}
}
.avatar {
width:32px;
}
.event_icon {
float: right;
border: 1px solid #EEE;
padding: 5px;
@include border-radius(5px);
background: #F9F9F9;
img {
width:20px;
}
}
ul {
margin-left:50px;
margin-bottom:5px;
.avatar {
width:18px;
margin-top:3px;
}
}
padding: 15px 5px;
&:last-child { border:none }
.wll:hover { background:none }
.event_commits {
margin-top: 5px;
li {
&.commit {
background: transparent;
padding:3px;
border:none;
font-size:12px;
}
&.commits-stat {
display: block;
margin-top: 5px;
}
}
}
}
/**
* Push event widget
*
*/
.event_lp {
@extend .ui-box;
color:#777;
margin-bottom:20px;
padding:8px;
@include border-radius(4px);
min-height:22px;
.avatar {
width:24px;
}
}

View file

@ -43,7 +43,9 @@
padding: 8px 0;
overflow: hidden;
display: block;
position:relative;
img {float: left; margin-right: 10px;}
img.emoji {float:none;margin:0;}
.note-author cite{font-style: italic;}
p { color:$style_color; }
.note-author { color: $style_color;}
@ -55,7 +57,9 @@
.delete-note {
display:none;
float:right;
position:absolute;
right:0;
top:0;
}
&:hover {

View file

@ -30,7 +30,7 @@ class Admin::UsersController < AdminController
def new
@admin_user = User.new(projects_limit: Gitlab.config.default_projects_limit)
@admin_user = User.new({ projects_limit: Gitlab.config.default_projects_limit }, as: :admin)
end
def edit
@ -60,7 +60,7 @@ class Admin::UsersController < AdminController
def create
admin = params[:user].delete("admin")
@admin_user = User.new(params[:user])
@admin_user = User.new(params[:user], as: :admin)
@admin_user.admin = (admin && admin.to_i > 0)
respond_to do |format|
@ -86,7 +86,7 @@ class Admin::UsersController < AdminController
@admin_user.admin = (admin && admin.to_i > 0)
respond_to do |format|
if @admin_user.update_attributes(params[:user])
if @admin_user.update_attributes(params[:user], as: :admin)
format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' }
format.json { head :ok }
else

View file

@ -52,6 +52,7 @@ class CommitsController < ApplicationController
@commits = result[:commits]
@commit = result[:commit]
@diffs = result[:diffs]
@refs_are_same = result[:same]
@line_notes = []
@commits = CommitDecorator.decorate(@commits)

View file

@ -1,5 +1,4 @@
class HooksController < ApplicationController
before_filter :authenticate_user!
before_filter :project
layout "project"

View file

@ -1,5 +1,4 @@
class IssuesController < ApplicationController
before_filter :authenticate_user!
before_filter :project
before_filter :module_enabled
before_filter :issue, only: [:edit, :update, :destroy, :show]

View file

@ -1,5 +1,4 @@
class LabelsController < ApplicationController
before_filter :authenticate_user!
before_filter :project
before_filter :module_enabled

View file

@ -1,5 +1,4 @@
class MergeRequestsController < ApplicationController
before_filter :authenticate_user!
before_filter :project
before_filter :module_enabled
before_filter :merge_request, only: [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check, :raw]

View file

@ -1,5 +1,4 @@
class MilestonesController < ApplicationController
before_filter :authenticate_user!
before_filter :project
before_filter :module_enabled
before_filter :milestone, only: [:edit, :update, :destroy, :show]

View file

@ -1,5 +1,4 @@
class SnippetsController < ApplicationController
before_filter :authenticate_user!
before_filter :project
before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw]
layout "project"

View file

@ -32,7 +32,11 @@ module TreeHelper
#
# Returns boolean
def markup?(filename)
filename.end_with?(*%w(.mdown .md .markdown .textile .rdoc .org .creole
filename.end_with?(*%w(.textile .rdoc .org .creole
.mediawiki .rst .asciidoc .pod))
end
def gitlab_markdown?(filename)
filename.end_with?(*%w(.mdown .md .markdown))
end
end

View file

@ -82,20 +82,24 @@ class Commit
end
def compare(project, from, to)
first = project.commit(to.try(:strip))
last = project.commit(from.try(:strip))
result = {
commits: [],
diffs: [],
commit: nil
commit: nil,
same: false
}
return result unless from && to
first = project.commit(to.try(:strip))
last = project.commit(from.try(:strip))
if first && last
commits = [first, last].sort_by(&:created_at)
younger = commits.first
older = commits.last
result[:same] = (younger.id == older.id)
result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)}
result[:diffs] = project.repo.diff(younger.id, older.id) rescue []
result[:commit] = Commit.new(older)

View file

@ -132,6 +132,7 @@ class Event < ActiveRecord::Base
end
end
delegate :name, :email, to: :author, prefix: true, allow_nil: true
delegate :title, to: :issue, prefix: true, allow_nil: true
delegate :title, to: :merge_request, prefix: true, allow_nil: true

View file

@ -6,8 +6,9 @@ class User < ActiveRecord::Base
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio,
:name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme,
:theme_id, :force_random_password, :extern_uid, :provider
:name, :skype, :linkedin, :twitter, :dark_scheme,
:theme_id, :force_random_password, :extern_uid, :provider, :as => [:default, :admin]
attr_accessible :projects_limit, :as => :admin
attr_accessor :force_random_password

View file

@ -79,6 +79,14 @@ module Repository
@heads ||= repo.heads
end
def branches_names
heads.map(&:name)
end
def ref_names
[branches_names + tags].flatten
end
def tree(fcommit, path = nil)
fcommit = commit if fcommit == :head
tree = fcommit.tree

View file

@ -1,16 +1,16 @@
= render "head"
%h3
%h3.page_title
Compare View
%hr
%div
%p
%p.slead
Fill input field with commit id like
%code '4eedf23'
%code.label_branch 4eedf23
or branch/tag name like
%code master
&amp; press compare button for commits list, code diff.
%code.label_branch master
and press compare button for commits list, code diff.
%br
@ -19,22 +19,24 @@
= text_field_tag :from, params[:from], placeholder: "master", class: "xlarge"
= "..."
= text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge"
- if @refs_are_same
.alert
%span Refs are the same
.actions
= submit_tag "Compare", class: "btn primary"
= submit_tag "Compare", class: "btn primary wide commits-compare-btn"
- unless @commits.empty?
- if @commits.present?
%div.ui-box
%h5.small Commits (#{@commits.count})
%ul.unstyled= render @commits
- unless @diffs.empty?
- unless @diffs.empty?
%h4 Diff
= render "commits/diffs", diffs: @diffs
:javascript
$(function() {
var availableTags = #{@project.heads.map(&:name).to_json};
var availableTags = #{@project.ref_names.to_json};
$("#from").autocomplete({
source: availableTags,
@ -45,5 +47,7 @@
source: availableTags,
minLength: 1
});
disableButtonIfEmptyField('#to', '.commits-compare-btn');
});

View file

@ -2,7 +2,7 @@
%li.commit
%p
= link_to commit.short_id(8), project_commit_path(project, id: commit.id), class: "commit_short_id"
%strong.cdark= commit.author_name
%span= commit.author_name
&ndash;
= image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
= gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding"

View file

@ -1,17 +1,18 @@
- if event.allowed?
%div.event-item
- if event.issue?
.event_feed
= render "events/event_issue", event: event
- elsif event.merge_request?
.event_feed
= render "events/event_merge_request", event: event
- elsif event.push?
.event_feed
= render "events/event_push", event: event
- elsif event.membership_changed?
.event_feed
= render "events/event_membership_changed", event: event
%span.cgray.right
= time_ago_in_words(event.created_at)
ago.
.clearfix

View file

@ -1,11 +1,8 @@
= image_tag gravatar_icon(event.author_email), class: "avatar"
%strong #{event.author_name}
%span.event_label{class: event.action_name}= event.action_name
issue
= link_to project_issue_path(event.project, event.issue) do
.event-title
%strong.author_name #{event.author_name}
%span.event_label{class: event.action_name} #{event.action_name} issue
= link_to project_issue_path(event.project, event.issue) do
%strong= truncate event.issue_title
at
%strong= link_to event.project.name, event.project
%span.cgray
= time_ago_in_words(event.created_at)
ago.
at
%strong= link_to event.project.name, event.project

View file

@ -2,7 +2,7 @@
.event_lp
%div
= image_tag gravatar_icon(event.author_email), class: "avatar"
%span Your pushed to
%span You pushed to
= event.ref_type
= link_to project_commits_path(event.project, ref: event.ref_name) do
%strong= truncate(event.ref_name, length: 28)

View file

@ -1,9 +1,9 @@
= image_tag gravatar_icon(event.author_email), class: "avatar"
%strong #{event.author_name}
%span.event_label{class: event.action_name}= event.action_name
project
%strong= link_to event.project_name, event.project
%span.cgray
.event-title
%strong.author_name #{event.author_name}
%span.event_label{class: event.action_name} #{event.action_name} project
%strong= link_to event.project_name, event.project
%span.cgray
= time_ago_in_words(event.created_at)
ago.

View file

@ -1,18 +1,16 @@
- if event.action_name == "merged"
.event_icon= image_tag "event_mr_merged.png"
= image_tag gravatar_icon(event.author_email), class: "avatar"
%strong #{event.author_name}
%span.event_label{class: event.action_name}= event.action_name
merge request
= link_to project_merge_request_path(event.project, event.merge_request) do
.event-title
%strong.author_name #{event.author_name}
%span.event_label{class: event.action_name} #{event.action_name} merge request
= link_to project_merge_request_path(event.project, event.merge_request) do
%strong= truncate event.merge_request_title
at
%strong= link_to event.project.name, event.project
%span.cgray
= time_ago_in_words(event.created_at)
ago.
%br
%span= event.merge_request.source_branch
&rarr;
%span= event.merge_request.target_branch
at
%strong= link_to event.project.name, event.project
.event-body
.event-info
%span= event.merge_request.source_branch
&rarr;
%span= event.merge_request.target_branch

View file

@ -1,30 +1,26 @@
%div
.event_icon= image_tag "event_push.png"
= image_tag gravatar_icon(event.author_email), class: "avatar"
%strong #{event.author_name}
%span.event_label.pushed= event.push_action_name
= event.ref_type
.event-title
%strong.author_name #{event.author_name}
%span.event_label.pushed #{event.push_action_name} #{event.ref_type}
= link_to project_commits_path(event.project, ref: event.ref_name) do
%strong= event.ref_name
at
%strong= link_to event.project.name, event.project
%span.cgray
= time_ago_in_words(event.created_at)
ago.
- if event.push_with_commits?
- if event.commits_count > 1
= link_to compare_project_commits_path(event.project, from: event.parent_commit.id, to: event.last_commit.id) do
%strong #{event.parent_commit.id[0..7]}...#{event.last_commit.id[0..7]}
- project = event.project
.event-body
%ul.unstyled.event_commits
- if event.commits_count > 3
- event.commits[0...2].each do |commit|
= render "events/commit", commit: commit, project: project
%li
%br
\... and #{event.commits_count - 2} more commits
- else
- event.commits.each do |commit|
- few_commits = event.commits[0...2]
- few_commits.each do |commit|
= render "events/commit", commit: commit, project: project
%li.commits-stat
- if event.commits_count > 2
%span ... and #{event.commits_count - 2} more commits.
= link_to compare_project_commits_path(event.project, from: event.parent_commit.id, to: event.last_commit.id) do
%strong Compare &rarr; #{event.parent_commit.id[0..7]}...#{event.last_commit.id[0..7]}
.clearfix

View file

@ -1,24 +1,30 @@
%h3 API
%h3.page_title API
.back_link
= link_to help_path do
&larr; to index
%hr
%br
%ol
%ul.nav.nav-tabs.log-tabs
%li.active
= link_to "README", "#README", 'data-toggle' => 'tab'
%li
%a{href: "#README"} README
= link_to "Projects", "#projects", 'data-toggle' => 'tab'
%li
%a{href: "#projects"} Projects
= link_to "Snippets", "#snippets", 'data-toggle' => 'tab'
%li
%a{href: "#snippets"} Snippets
= link_to "Repositories", "#repositories", 'data-toggle' => 'tab'
%li
%a{href: "#users"} Users
= link_to "Users", "#users", 'data-toggle' => 'tab'
%li
%a{href: "#issues"} Issues
= link_to "Session", "#session", 'data-toggle' => 'tab'
%li
%a{href: "#milestones"} Milestones
= link_to "Issues", "#issues", 'data-toggle' => 'tab'
%li
= link_to "Milestones", "#milestones", 'data-toggle' => 'tab'
.file_holder#README
.tab-content
.tab-pane.active#README
.file_holder
.file_title
%i.icon-file
README
@ -26,9 +32,8 @@
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "README.md"))
%br
.file_holder#projects
.tab-pane#projects
.file_holder
.file_title
%i.icon-file
Projects
@ -36,9 +41,8 @@
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "projects.md"))
%br
.file_holder#snippets
.tab-pane#snippets
.file_holder
.file_title
%i.icon-file
Projects Snippets
@ -46,9 +50,17 @@
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "snippets.md"))
%br
.tab-pane#repositories
.file_holder
.file_title
%i.icon-file
Projects
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "repositories.md"))
.file_holder#users
.tab-pane#users
.file_holder
.file_title
%i.icon-file
Users
@ -56,9 +68,17 @@
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "users.md"))
%br
.tab-pane#session
.file_holder
.file_title
%i.icon-file
Session
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "session.md"))
.file_holder#issues
.tab-pane#issues
.file_holder
.file_title
%i.icon-file
Issues
@ -66,9 +86,8 @@
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "issues.md"))
%br
.file_holder#milestones
.tab-pane#milestones
.file_holder
.file_title
%i.icon-file
Milestones

View file

@ -37,3 +37,14 @@
= f.file_field :attachment, class: "input-file"
%span.hint Any file less than 10 MB
:javascript
$(function(){
var names = #{@project.users.pluck(:name)}, emoji = ['+1', '-1'];
var emoji = $.map(emoji, function(value, i) {return {key:value + ':', name:value}});
$('#note_note').
atWho('@', { data: names }).
atWho(':', {
data: emoji,
tpl: "<li data-value='${key}'>${name} #{escape_javascript image_tag('emoji/${name}.png', :size => '20x20')}</li>"
});
});

View file

@ -43,6 +43,10 @@
%i.icon-file
= content.name
.file_content.wiki
- if gitlab_markdown?(content.name)
= preserve do
= markdown(content.data)
- else
= raw GitHub::Markup.render(content.name, content.data)
:javascript

View file

@ -9,7 +9,11 @@
= link_to "history", project_commits_path(@project, path: params[:path], ref: @ref), class: "btn very_small"
= link_to "blame", blame_file_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small"
- if file.text?
- if markup?(name)
- if gitlab_markdown?(name)
.file_content.wiki
= preserve do
= markdown(file.data)
- elsif markup?(name)
.file_content.wiki
= raw GitHub::Markup.render(name, file.data)
- else

View file

@ -0,0 +1,31 @@
# Custom Redis configuration
rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
rails_env = ENV['RAILS_ENV'] || 'development'
config_file = File.join(rails_root, 'config', 'resque.yml')
if File.exists?(config_file)
resque_config = YAML.load_file(config_file)
Resque.redis = resque_config[rails_env]
end
# Queues
Resque.watch_queue(PostReceive.instance_variable_get("@queue"))
# Authentication
require 'resque/server'
class Authentication
def initialize(app)
@app = app
end
def call(env)
account = env['warden'].authenticate!(:database_authenticatable, :rememberable, scope: :user)
raise "Access denied" if !account.admin?
@app.call(env)
end
end
Resque::Server.use Authentication
# Mailer
Resque::Mailer.excluded_environments = []

View file

@ -1 +0,0 @@
Resque.watch_queue(PostReceive.instance_variable_get("@queue"))

View file

@ -1,8 +0,0 @@
rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
rails_env = ENV['RAILS_ENV'] || 'development'
config_file = File.join(rails_root, 'config', 'resque.yml')
if File.exists?(config_file)
resque_config = YAML.load_file(config_file)
Resque.redis = resque_config[rails_env]
end

View file

@ -1,14 +0,0 @@
require 'resque/server'
class Authentication
def initialize(app)
@app = app
end
def call(env)
account = env['warden'].authenticate!(:database_authenticatable, :rememberable, scope: :user)
raise "Access denied" if !account.admin?
@app.call(env)
end
end
Resque::Server.use Authentication

View file

@ -1 +0,0 @@
Resque::Mailer.excluded_environments = []

View file

@ -6,7 +6,7 @@ working_directory app_dir
# worker spawn times
preload_app true
# nuke workers after 60 seconds (the default)
# nuke workers after 30 seconds (60 is the default)
timeout 30
# listen on a Unix domain socket and/or a TCP port,

View file

@ -30,8 +30,9 @@ When listing resources you can pass the following parameters:
## Contents
+ [Users](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/users.md)
+ [Session](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/session.md)
+ [Projects](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md)
+ [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md)
+ [Repositories](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/repositories.md)
+ [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md)
+ [Milestones](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/milestones.md)
+ [SSH Keys](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/keys.md)

View file

@ -1,79 +0,0 @@
## List keys
Get a list of currently authenticated user's keys.
```
GET /keys
```
```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="
}
]
```
## Single key
Get a single key.
```
GET /keys/:id
```
Parameters:
+ `id` (required) - The ID of a key
```json
{
"id": 1,
"title" : "Public key"
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="
}
```
## Add key
Create new key owned by currently authenticated user
```
POST /keys
```
Parameters:
+ `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 key
Delete key owned by currently authenticated user
```
DELETE /keys/:id
```
Parameters:
+ `id` (required) - key ID
Will return `200 OK` on success, or `404 Not Found` on fail.

View file

@ -102,7 +102,7 @@ Parameters:
+ `name` (required) - new project name
+ `code` (optional) - new project code, uses project name if not set
+ `path` (optional) - new project path, uses project name if not set
+ `description (optional) - short project description
+ `description` (optional) - short project description
+ `default_branch` (optional) - 'master' by default
+ `issues_enabled` (optional) - enabled by default
+ `wall_enabled` (optional) - enabled by default
@ -112,66 +112,89 @@ Parameters:
Will return created project with status `201 Created` on success, or `404 Not
found` on fail.
## Get project users
## List project team members
Get users and access roles for existing project
Get a list of project team members.
```
GET /projects/:id/users
GET /projects/:id/members
```
Parameters:
+ `id` (required) - The ID or code name of a project
Will return users and their access roles with status `200 OK` on success, or `404 Not found` on fail.
## Get project team member
## Add project users
Add users to exiting project
Get a project team member.
```
POST /projects/:id/users
GET /projects/:id/members/:user_id
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `user_ids` (required) - The ID list of users to add
+ `project_access` (required) - Project access level
+ `user_id` (required) - The ID of a user
```json
{
"id": 1,
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
"created_at": "2012-05-23T08:00:58Z",
"access_level": 40
}
```
## Add project team member
Add a user to a project team.
```
POST /projects/:id/members
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `user_id` (required) - The ID of a user to add
+ `access_level` (required) - Project access level
Will return status `201 Created` on success, or `404 Not found` on fail.
## Update project users access level
## Edit project team member
Update existing users to specified access level
Update project team member to specified access level.
```
PUT /projects/:id/users
PUT /projects/:id/members/:user_id
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `user_ids` (required) - The ID list of users to add
+ `project_access` (required) - Project access level
+ `user_id` (required) - The ID of a team member
+ `access_level` (required) - Project access level
Will return status `200 OK` on success, or `404 Not found` on fail.
## Delete project users
## Remove project team member
Delete users from exiting project
Removes user from project team.
```
DELETE /projects/:id/users
DELETE /projects/:id/members/:user_id
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `user_ids` (required) - The ID list of users to add
+ `user_id` (required) - The ID of a team member
Will return status `200 OK` on success, or `404 Not found` on fail.
Status code `200` will be returned on success.
## Get project hooks
@ -216,135 +239,3 @@ Parameters:
+ `hook_id` (required) - The ID of hook to delete
Will return status `200 OK` on success, or `404 Not found` on fail.
## Project repository branches
Get a list of repository branches from a project, sorted by name alphabetically.
```
GET /projects/:id/repository/branches
```
Parameters:
+ `id` (required) - The ID or code name of a project
```json
[
{
"name": "master",
"commit": {
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
"parents": [
{
"id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
}
],
"tree": "46e82de44b1061621357f24c05515327f2795a95",
"message": "add projects API",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "John Smith",
"email": "john@example.com"
},
"authored_date": "2012-06-27T05:51:39-07:00",
"committed_date": "2012-06-28T03:44:20-07:00"
}
}
]
```
Get a single project repository branch.
```
GET /projects/:id/repository/branches/:branch
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `branch` (required) - The name of the branch
```json
{
"name": "master",
"commit": {
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
"parents": [
{
"id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
}
],
"tree": "46e82de44b1061621357f24c05515327f2795a95",
"message": "add projects API",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "John Smith",
"email": "john@example.com"
},
"authored_date": "2012-06-27T05:51:39-07:00",
"committed_date": "2012-06-28T03:44:20-07:00"
}
}
```
## Project repository tags
Get a list of repository tags from a project, sorted by name in reverse alphabetical order.
```
GET /projects/:id/repository/tags
```
Parameters:
+ `id` (required) - The ID or code name of a project
```json
[
{
"name": "v1.0.0",
"commit": {
"id": "2695effb5807a22ff3d138d593fd856244e155e7",
"parents": [
],
"tree": "38017f2f189336fe4497e9d230c5bb1bf873f08d",
"message": "Initial commit",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "Jack Smith",
"email": "jack@example.com"
},
"authored_date": "2012-05-28T04:42:42-07:00",
"committed_date": "2012-05-28T04:42:42-07:00"
}
}
]
```
## Raw blob content
Get the raw file contents for a file.
```
GET /projects/:id/repository/commits/:sha/blob
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `sha` (required) - The commit or branch name
+ `filepath` (required) - The path the file
Will return the raw file contents.

166
doc/api/repositories.md Normal file
View file

@ -0,0 +1,166 @@
## Project repository branches
Get a list of repository branches from a project, sorted by name alphabetically.
```
GET /projects/:id/repository/branches
```
Parameters:
+ `id` (required) - The ID or code name of a project
```json
[
{
"name": "master",
"commit": {
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
"parents": [
{
"id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
}
],
"tree": "46e82de44b1061621357f24c05515327f2795a95",
"message": "add projects API",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "John Smith",
"email": "john@example.com"
},
"authored_date": "2012-06-27T05:51:39-07:00",
"committed_date": "2012-06-28T03:44:20-07:00"
}
}
]
```
## Project repository branch
Get a single project repository branch.
```
GET /projects/:id/repository/branches/:branch
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `branch` (required) - The name of the branch
```json
{
"name": "master",
"commit": {
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
"parents": [
{
"id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
}
],
"tree": "46e82de44b1061621357f24c05515327f2795a95",
"message": "add projects API",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "John Smith",
"email": "john@example.com"
},
"authored_date": "2012-06-27T05:51:39-07:00",
"committed_date": "2012-06-28T03:44:20-07:00"
}
}
```
## Project repository tags
Get a list of repository tags from a project, sorted by name in reverse alphabetical order.
```
GET /projects/:id/repository/tags
```
Parameters:
+ `id` (required) - The ID or code name of a project
```json
[
{
"name": "v1.0.0",
"commit": {
"id": "2695effb5807a22ff3d138d593fd856244e155e7",
"parents": [
],
"tree": "38017f2f189336fe4497e9d230c5bb1bf873f08d",
"message": "Initial commit",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "Jack Smith",
"email": "jack@example.com"
},
"authored_date": "2012-05-28T04:42:42-07:00",
"committed_date": "2012-05-28T04:42:42-07:00"
}
}
]
```
## Project repository commits
Get a list of repository commits in a project.
```
GET /projects/:id/repository/commits
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `ref_name` (optional) - The name of a repository branch or tag
```json
[
{
"id": "ed899a2f4b50b4370feeea94676502b42383c746",
"short_id": "ed899a2f4b5",
"title": "Replace sanitize with escape once",
"author_name": "Dmitriy Zaporozhets",
"author_email": "dzaporozhets@sphereconsultinginc.com",
"created_at": "2012-09-20T11:50:22+03:00"
},
{
"id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
"short_id": "6104942438c",
"title": "Sanitize for network graph",
"author_name": "randx",
"author_email": "dmitriy.zaporozhets@gmail.com",
"created_at": "2012-09-20T09:06:12+03:00"
}
]
```
## Raw blob content
Get the raw file contents for a file.
```
GET /projects/:id/repository/commits/:sha/blob
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `sha` (required) - The commit or branch name
+ `filepath` (required) - The path the file
Will return the raw file contents.

22
doc/api/session.md Normal file
View file

@ -0,0 +1,22 @@
Login to get private token
```
POST /session
```
Parameters:
+ `email` (required) - The email of user
+ `password` (required) - Valid password
```json
{
"id": 1,
"email": "john@example.com",
"name": "John Smith",
"private_token": "dd34asd13as",
"created_at": "2012-05-23T08:00:58Z",
"blocked": true
}
```

View file

@ -88,3 +88,81 @@ GET /user
"theme_id": 1
}
```
## List SSH keys
Get a list of currently authenticated user's SSH keys.
```
GET /user/keys
```
```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="
}
]
```
## Single SSH key
Get a single key.
```
GET /user/keys/:id
```
Parameters:
+ `id` (required) - The ID of an SSH key
```json
{
"id": 1,
"title" : "Public key"
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="
}
```
## Add SSH key
Create new key owned by currently authenticated user
```
POST /user/keys
```
Parameters:
+ `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
Delete key owned by currently authenticated user
```
DELETE /user/keys/:id
```
Parameters:
+ `id` (required) - SSH key ID
Will return `200 OK` on success, or `404 Not Found` on fail.

View file

@ -16,7 +16,7 @@ class Dashboard < Spinach::FeatureSteps
end
Then 'I should see last push widget' do
page.should have_content "Your pushed to branch new_design"
page.should have_content "You pushed to branch new_design"
page.should have_link "Create Merge Request"
end

View file

@ -17,6 +17,6 @@ module Gitlab
mount Projects
mount Issues
mount Milestones
mount Keys
mount Session
end
end

View file

@ -9,6 +9,10 @@ module Gitlab
expose :id, :email, :name, :blocked, :created_at
end
class UserLogin < UserBasic
expose :private_token
end
class Hook < Grape::Entity
expose :id, :url
end
@ -20,15 +24,20 @@ module Gitlab
expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at
end
class UsersProject < Grape::Entity
expose :user, using: Entities::UserBasic
expose :project_access
class ProjectMember < UserBasic
expose :project_access, :as => :access_level do |user, options|
options[:project].users_projects.find_by_user_id(user.id).project_access
end
end
class RepoObject < Grape::Entity
expose :name, :commit
end
class RepoCommit < Grape::Entity
expose :id, :short_id, :title, :author_name, :author_email, :created_at
end
class ProjectSnippet < Grape::Entity
expose :id, :title, :file_name
expose :author, using: Entities::UserBasic
@ -36,7 +45,9 @@ module Gitlab
end
class Milestone < Grape::Entity
expose :id, :title, :description, :due_date, :closed, :updated_at, :created_at
expose :id
expose (:project_id) {|milestone| milestone.project.id}
expose :title, :description, :due_date, :closed, :updated_at, :created_at
end
class Issue < Grape::Entity
@ -49,10 +60,8 @@ module Gitlab
expose :closed, :updated_at, :created_at
end
class Key < Grape::Entity
expose :id,
:title,
:key
class SSHKey < Grape::Entity
expose :id, :title, :key
end
end
end

View file

@ -1,50 +0,0 @@
module Gitlab
# Keys API
class Keys < Grape::API
before { authenticate! }
resource :keys do
# Get currently authenticated user's keys
#
# Example Request:
# GET /keys
get do
present current_user.keys, with: Entities::Key
end
# Get single key owned by currently authenticated user
#
# Example Request:
# GET /keys/:id
get "/:id" do
key = current_user.keys.find params[:id]
present key, with: Entities::Key
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 /keys
post do
attrs = attributes_for_keys [:title, :key]
key = current_user.keys.new attrs
if key.save
present key, with: Entities::Key
else
not_found!
end
end
# Delete existed ssh key of currently authenticated user
#
# Parameters:
# id (required) - SSH Key ID
# Example Request:
# DELETE /keys/:id
delete "/:id" do
key = current_user.keys.find params[:id]
key.delete
end
end
end
end

View file

@ -11,6 +11,8 @@ module Gitlab
# Example Request:
# GET /projects/:id/milestones
get ":id/milestones" do
authorize! :read_milestone, user_project
present paginate(user_project.milestones), with: Entities::Milestone
end
@ -22,6 +24,8 @@ module Gitlab
# Example Request:
# GET /projects/:id/milestones/:milestone_id
get ":id/milestones/:milestone_id" do
authorize! :read_milestone, user_project
@milestone = user_project.milestones.find(params[:milestone_id])
present @milestone, with: Entities::Milestone
end
@ -36,6 +40,8 @@ module Gitlab
# Example Request:
# POST /projects/:id/milestones
post ":id/milestones" do
authorize! :admin_milestone, user_project
attrs = attributes_for_keys [:title, :description, :due_date]
@milestone = user_project.milestones.new attrs
if @milestone.save

View file

@ -57,56 +57,83 @@ module Gitlab
end
end
# Get project users
# Get a project team members
#
# Parameters:
# id (required) - The ID or code name of a project
# Example Request:
# GET /projects/:id/users
get ":id/users" do
@users_projects = paginate user_project.users_projects
present @users_projects, with: Entities::UsersProject
# GET /projects/:id/members
get ":id/members" do
@members = paginate user_project.users
present @members, with: Entities::ProjectMember, project: user_project
end
# Add users to project with specified access level
# Get a project team members
#
# Parameters:
# id (required) - The ID or code name of a project
# user_ids (required) - The ID list of users to add
# project_access (required) - Project access level
# user_id (required) - The ID of a user
# Example Request:
# POST /projects/:id/users
post ":id/users" do
authorize! :admin_project, user_project
user_project.add_users_ids_to_team(params[:user_ids].values, params[:project_access])
nil
# GET /projects/:id/members/:user_id
get ":id/members/:user_id" do
@member = user_project.users.find params[:user_id]
present @member, with: Entities::ProjectMember, project: user_project
end
# Update users to specified access level
# Add a new project team member
#
# Parameters:
# id (required) - The ID or code name of a project
# user_ids (required) - The ID list of users to add
# project_access (required) - New project access level to
# user_id (required) - The ID of a user
# access_level (required) - Project access level
# Example Request:
# PUT /projects/:id/add_users
put ":id/users" do
# POST /projects/:id/members
post ":id/members" do
authorize! :admin_project, user_project
user_project.update_users_ids_to_role(params[:user_ids].values, params[:project_access])
nil
users_project = user_project.users_projects.new(
user_id: params[:user_id],
project_access: params[:access_level]
)
if users_project.save
@member = users_project.user
present @member, with: Entities::ProjectMember, project: user_project
else
not_found!
end
end
# Delete project users
# Update project team member
#
# Parameters:
# id (required) - The ID or code name of a project
# user_ids (required) - The ID list of users to delete
# user_id (required) - The ID of a team member
# access_level (required) - Project access level
# Example Request:
# DELETE /projects/:id/users
delete ":id/users" do
# PUT /projects/:id/members/:user_id
put ":id/members/:user_id" do
authorize! :admin_project, user_project
user_project.delete_users_ids_from_team(params[:user_ids].values)
nil
users_project = user_project.users_projects.find_by_user_id params[:user_id]
if users_project.update_attributes(project_access: params[:access_level])
@member = users_project.user
present @member, with: Entities::ProjectMember, project: user_project
else
not_found!
end
end
# Remove a team member from project
#
# Parameters:
# id (required) - The ID or code name of a project
# user_id (required) - The ID of a team member
# Example Request:
# DELETE /projects/:id/members/:user_id
delete ":id/members/:user_id" do
authorize! :admin_project, user_project
users_project = user_project.users_projects.find_by_user_id params[:user_id]
users_project.destroy
end
# Get project hooks
@ -184,6 +211,24 @@ module Gitlab
present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject
end
# Get a project repository commits
#
# Parameters:
# id (required) - The ID or code name of a project
# ref_name (optional) - The name of a repository branch or tag
# Example Request:
# GET /projects/:id/repository/commits
get ":id/repository/commits" do
authorize! :download_code, user_project
page = params[:page] || 0
per_page = params[:per_page] || 20
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
commits = user_project.commits(ref, nil, per_page, page * per_page)
present CommitDecorator.decorate(commits), with: Entities::RepoCommit
end
# Get a project snippet
#
# Parameters:
@ -207,6 +252,8 @@ module Gitlab
# Example Request:
# POST /projects/:id/snippets
post ":id/snippets" do
authorize! :write_snippet, user_project
attrs = attributes_for_keys [:title, :file_name]
attrs[:expires_at] = params[:lifetime] if params[:lifetime].present?
attrs[:content] = params[:code] if params[:code].present?
@ -282,6 +329,8 @@ module Gitlab
# Example Request:
# GET /projects/:id/repository/commits/:sha/blob
get ":id/repository/commits/:sha/blob" do
authorize! :download_code, user_project
ref = params[:sha]
commit = user_project.commit ref

20
lib/api/session.rb Normal file
View file

@ -0,0 +1,20 @@
module Gitlab
# Users API
class Session < Grape::API
# Login to get token
#
# Example Request:
# POST /session
post "/session" do
resource = User.find_for_database_authentication(email: params[:email])
return unauthorized! unless resource
if resource.valid_password?(params[:password])
present resource, with: Entities::UserLogin
else
unauthorized!
end
end
end
end

View file

@ -25,12 +25,59 @@ module Gitlab
end
end
resource :user do
# Get currently authenticated user
#
# Example Request:
# GET /user
get "/user" do
get do
present @current_user, with: Entities::User
end
# Get currently authenticated user's keys
#
# Example Request:
# GET /user/keys
get "keys" do
present current_user.keys, with: Entities::SSHKey
end
# Get single key owned by currently authenticated user
#
# Example Request:
# GET /user/keys/:id
get "keys/:id" do
key = current_user.keys.find params[: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 /user/keys
post "keys" do
attrs = attributes_for_keys [:title, :key]
key = current_user.keys.new attrs
if key.save
present key, with: Entities::SSHKey
else
not_found!
end
end
# Delete existed ssh key of currently authenticated user
#
# Parameters:
# id (required) - SSH Key ID
# Example Request:
# DELETE /user/keys/:id
delete "keys/:id" do
key = current_user.keys.find params[:id]
key.delete
end
end
end
end

View file

@ -5,7 +5,7 @@ module Gitlab
attr_accessor :time, :space
attr_accessor :refs
include ActionView::Helpers::SanitizeHelper
include ActionView::Helpers::TagHelper
def self.to_graph(project)
@repo = project.repo
@ -166,7 +166,7 @@ module Gitlab
h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?
h[:id] = sha
h[:date] = date
h[:message] = sanitize(Gitlab::Encode.utf8(message))
h[:message] = escape_once(Gitlab::Encode.utf8(message))
h[:login] = author.email
h
end

View file

@ -1,26 +1,20 @@
desc "Add all users to all projects, system administratos are added as masters"
desc "Add all users to all projects (admin users are added as masters)"
task :add_users_to_project_teams => :environment do |t, args|
users = User.find_all_by_admin(false, :select => 'id').map(&:id)
admins = User.find_all_by_admin(true, :select => 'id').map(&:id)
user_ids = User.where(:admin => false).pluck(:id)
admin_ids = User.where(:admin => true).pluck(:id)
users.each do |user|
puts "#{user}"
end
Project.all.each do |project|
puts "Importing #{users.length} users into #{project.path}"
UsersProject.bulk_import(project, users, UsersProject::DEVELOPER)
puts "Importing #{admins.length} admins into #{project.path}"
UsersProject.bulk_import(project, admins, UsersProject::MASTER)
Project.find_each do |project|
puts "Importing #{user_ids.size} users into #{project.code}"
UsersProject.bulk_import(project, user_ids, UsersProject::DEVELOPER)
puts "Importing #{admin_ids.size} admins into #{project.code}"
UsersProject.bulk_import(project, admin_ids, UsersProject::MASTER)
end
end
desc "Add user to as a developer to all projects"
task :add_user_to_project_teams, [:email] => :environment do |t, args|
user_email = args.email
user = User.find_by_email(user_email)
user = User.find_by_email args.email
project_ids = Project.pluck(:id)
project_ids = Project.all.map(&:id)
UsersProject.user_bulk_import(user,project_ids,UsersProject::DEVELOPER)
UsersProject.user_bulk_import(user, project_ids, UsersProject::DEVELOPER)
end

View file

@ -1,20 +1,17 @@
desc "Imports existing Git repos from a directory into new projects in git_base_path"
task :import_projects, [:directory,:email] => :environment do |t, args|
user_email = args.email
import_directory = args.directory
user_email, import_directory = args.email, args.directory
repos_to_import = Dir.glob("#{import_directory}/*")
git_base_path = Gitlab.config.git_base_path
puts "Found #{repos_to_import.length} repos to import"
imported_count, skipped_count, failed_count = 0
puts "Found #{repos_to_import.size} repos to import"
imported_count = 0
skipped_count = 0
failed_count = 0
repos_to_import.each do |repo_path|
repo_name = File.basename repo_path
clone_path = "#{git_base_path}#{repo_name}.git"
puts " Processing #{repo_name}"
clone_path = "#{git_base_path}#{repo_name}.git"
if Dir.exists? clone_path
if Project.find_by_code(repo_name)
@ -38,7 +35,6 @@ task :import_projects, [:directory,:email] => :environment do |t, args|
else
failed_count += 1
end
end
puts "Finished importing #{imported_count} projects (skipped #{skipped_count}, failed #{failed_count})."
@ -49,63 +45,39 @@ def clone_bare_repo_as_git(existing_path, new_path)
git_user = Gitlab.config.ssh_user
begin
sh "sudo -u #{git_user} -i git clone --bare '#{existing_path}' #{new_path}"
true
rescue Exception=> msg
puts " ERROR: Faild to clone #{existing_path} to #{new_path}"
rescue Exception => msg
puts " ERROR: Failed to clone #{existing_path} to #{new_path}"
puts " Make sure #{git_user} can reach #{existing_path}"
puts " Exception-MSG: #{msg}"
false
end
end
# Creats a project in Gitlag given a @project_name@ to use (for name, web url, and code
# url) and a @user_email@ that will be assigned as the owner of the project.
# Creates a project in GitLab given a `project_name` to use
# (for name, web url, and code url) and a `user_email` that will be
# assigned as the owner of the project.
def create_repo_project(project_name, user_email)
user = User.find_by_email(user_email)
if user
if user = User.find_by_email(user_email)
# Using find_by_code since that's the most important identifer to be unique
if Project.find_by_code(project_name)
puts " INFO: Project #{project_name} already exists in Gitlab, skipping."
false
else
project = nil
if Project.find_by_code(project_name)
puts " ERROR: Project already exists #{project_name}"
return false
project = Project.find_by_code(project_name)
else
project = Project.create(
name: project_name,
code: project_name,
path: project_name,
owner: user,
description: "Automatically created from Rake on #{Time.now.to_s}"
description: "Automatically created from 'import_projects' rake task on #{Time.now}"
)
end
unless project.valid?
puts " ERROR: Failed to create project #{project} because #{project.errors.first}"
return false
end
# Add user as admin for project
project.users_projects.create!(
:project_access => UsersProject::MASTER,
:user => user
)
# Per projects_controller.rb#37
project.update_repository
if project.valid?
true
# Add user as admin for project
project.users_projects.create!(:project_access => UsersProject::MASTER, :user => user)
project.update_repository
else
puts " ERROR: Failed to create project #{project} because #{project.errors.first}"
false
end
end
else
puts " ERROR: #{user_email} not found, skipping"
false
puts " ERROR: user with #{user_email} not found, skipping"
end
end

View file

@ -2,22 +2,20 @@ require 'active_record/fixtures'
namespace :gitlab do
namespace :app do
# Create backup of gitlab system
desc "GITLAB | Create a backup of the gitlab system"
# Create backup of GitLab system
desc "GITLAB | Create a backup of the GitLab system"
task :backup_create => :environment do
Rake::Task["gitlab:app:db_dump"].invoke
Rake::Task["gitlab:app:repo_dump"].invoke
Dir.chdir(Gitlab.config.backup_path)
# saving additional informations
s = Hash.new
s["db_version"] = "#{ActiveRecord::Migrator.current_version}"
s["backup_created_at"] = "#{Time.now}"
s["gitlab_version"] = %x{git rev-parse HEAD}.gsub(/\n/,"")
s["tar_version"] = %x{tar --version | head -1}.gsub(/\n/,"")
s = {}
s[:db_version] = "#{ActiveRecord::Migrator.current_version}"
s[:backup_created_at] = "#{Time.now}"
s[:gitlab_version] = %x{git rev-parse HEAD}.gsub(/\n/,"")
s[:tar_version] = %x{tar --version | head -1}.gsub(/\n/,"")
File.open("#{Gitlab.config.backup_path}/backup_information.yml", "w+") do |file|
file << s.to_yaml.gsub(/^---\n/,'')
@ -32,7 +30,7 @@ namespace :gitlab do
end
# cleanup: remove tmp files
print "Deletion of tmp directories..."
print "Deleting tmp directories..."
if Kernel.system("rm -rf repositories/ db/ backup_information.yml")
puts "[DONE]".green
else
@ -52,26 +50,23 @@ namespace :gitlab do
else
puts "[SKIPPING]".yellow
end
end
# Restore backup of gitlab system
# Restore backup of GitLab system
desc "GITLAB | Restore a previously created backup"
task :backup_restore => :environment do
Dir.chdir(Gitlab.config.backup_path)
# check for existing backups in the backup dir
file_list = Dir.glob("*_gitlab_backup.tar").each.map { |f| f.split(/_/).first.to_i }
puts "no backup found" if file_list.count == 0
puts "no backups found" if file_list.count == 0
if file_list.count > 1 && ENV["BACKUP"].nil?
puts "Found more than one backup, please specify which one you want to restore:"
puts "rake gitlab:app:backup_restore BACKUP=timestamp_of_backup"
exit 1;
end
tar_file = ENV["BACKUP"].nil? ? File.join(file_list.first.to_s + "_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar")
tar_file = ENV["BACKUP"].nil? ? File.join("#{file_list.first}_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar")
unless File.exists?(tar_file)
puts "The specified backup doesn't exist!"
@ -102,16 +97,14 @@ namespace :gitlab do
Rake::Task["gitlab:app:repo_restore"].invoke
# cleanup: remove tmp files
print "Deletion of tmp directories..."
print "Deleting tmp directories..."
if Kernel.system("rm -rf repositories/ db/ backup_information.yml")
puts "[DONE]".green
else
puts "[FAILED]".red
end
end
################################################################################
################################# invoked tasks ################################
@ -121,7 +114,7 @@ namespace :gitlab do
backup_path_repo = File.join(Gitlab.config.backup_path, "repositories")
FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo)
puts "Dumping repositories:"
project = Project.all.map { |n| [n.path,n.path_to_repo] }
project = Project.all.map { |n| [n.path, n.path_to_repo] }
project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")]
project.each do |project|
print "- Dumping repository #{project.first}... "
@ -136,11 +129,11 @@ namespace :gitlab do
task :repo_restore => :environment do
backup_path_repo = File.join(Gitlab.config.backup_path, "repositories")
puts "Restoring repositories:"
project = Project.all.map { |n| [n.path,n.path_to_repo] }
project = Project.all.map { |n| [n.path, n.path_to_repo] }
project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")]
project.each do |project|
print "- Restoring repository #{project.first}... "
FileUtils.rm_rf(project.second) if File.dirname(project.second) # delet old stuff
FileUtils.rm_rf(project.second) if File.dirname(project.second) # delete old stuff
if Kernel.system("cd #{File.dirname(project.second)} > /dev/null 2>&1 && git clone --bare #{backup_path_repo}/#{project.first}.bundle #{project.first}.git > /dev/null 2>&1")
permission_commands = [
"sudo chmod -R g+rwX #{Gitlab.config.git_base_path}",
@ -158,7 +151,8 @@ namespace :gitlab do
task :db_dump => :environment do
backup_path_db = File.join(Gitlab.config.backup_path, "db")
FileUtils.mkdir_p(backup_path_db) until Dir.exists?(backup_path_db)
FileUtils.mkdir_p(backup_path_db) unless Dir.exists?(backup_path_db)
puts "Dumping database tables:"
ActiveRecord::Base.connection.tables.each do |tbl|
print "- Dumping table #{tbl}... "
@ -177,8 +171,10 @@ namespace :gitlab do
task :db_restore=> :environment do
backup_path_db = File.join(Gitlab.config.backup_path, "db")
puts "Restoring database tables:"
Rake::Task["db:reset"].invoke
Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir|
fixture_file = File.basename(dir, ".*" )
print "- Loading fixture #{fixture_file}..."

View file

@ -4,8 +4,7 @@ namespace :gitlab do
task :update_repos => :environment do
puts "Starting Projects"
Project.find_each(:batch_size => 100) do |project|
puts
puts "=== #{project.name}"
puts "\n=== #{project.name}"
project.update_repository
puts
end

View file

@ -8,4 +8,3 @@ namespace :gitlab do
]
end
end

View file

@ -1,12 +1,12 @@
namespace :gitlab do
namespace :app do
desc "GITLAB | Check gitlab installation status"
desc "GITLAB | Check GitLab installation status"
task :status => :environment do
puts "Starting diagnostic".yellow
puts "Starting diagnostics".yellow
git_base_path = Gitlab.config.git_base_path
print "config/database.yml............"
if File.exists?(File.join Rails.root, "config", "database.yml")
if File.exists?(Rails.root.join "config", "database.yml")
puts "exists".green
else
puts "missing".red
@ -14,7 +14,7 @@ namespace :gitlab do
end
print "config/gitlab.yml............"
if File.exists?(File.join Rails.root, "config", "gitlab.yml")
if File.exists?(Rails.root.join "config", "gitlab.yml")
puts "exists".green
else
puts "missing".red
@ -49,7 +49,7 @@ namespace :gitlab do
end
print "UMASK for .gitolite.rc is 0007? ............"
unless open("#{git_base_path}/../.gitolite.rc").grep(/UMASK([ \t]*)=([ \t>]*)0007/).empty?
if open("#{git_base_path}/../.gitolite.rc").grep(/UMASK([ \t]*)=([ \t>]*)0007/).any?
puts "YES".green
else
puts "NO".red
@ -69,16 +69,15 @@ namespace :gitlab do
end
end
if Project.count > 0
puts "Validating projects repositories:".yellow
Project.find_each(:batch_size => 100) do |project|
print "#{project.name}....."
hook_file = File.join(project.path_to_repo, 'hooks','post-receive')
hook_file = File.join(project.path_to_repo, 'hooks', 'post-receive')
unless File.exists?(hook_file)
puts "post-receive file missing".red
next
return
end
puts "post-receive file ok".green

View file

@ -4,7 +4,6 @@ namespace :gitlab do
task :write_hooks => :environment do
gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common")
gitlab_hooks_path = Rails.root.join("lib", "hooks")
gitlab_hook_files = ['post-receive']
gitlab_hook_files.each do |file_name|
@ -20,4 +19,3 @@ namespace :gitlab do
end
end
end

View file

@ -2,7 +2,7 @@ require 'spec_helper'
describe TreeHelper do
describe '#markup?' do
%w(mdown md markdown textile rdoc org creole mediawiki rst asciidoc pod).each do |type|
%w(textile rdoc org creole mediawiki rst asciidoc pod).each do |type|
it "returns true for #{type} files" do
markup?("README.#{type}").should be_true
end

View file

@ -73,4 +73,30 @@ describe User do
user.authentication_token.should_not be_blank
end
end
describe "attributes can be changed by a regular user" do
before do
@user = Factory :user
@user.update_attributes(skype: "testskype", linkedin: "testlinkedin")
end
it { @user.skype.should == 'testskype' }
it { @user.linkedin.should == 'testlinkedin' }
end
describe "attributes that shouldn't be changed by a regular user" do
before do
@user = Factory :user
@user.update_attributes(projects_limit: 50)
end
it { @user.projects_limit.should_not == 50 }
end
describe "attributes can be changed by an admin user" do
before do
@admin_user = Factory :admin
@admin_user.update_attributes({ skype: "testskype", projects_limit: 50 }, as: :admin)
end
it { @admin_user.skype.should == 'testskype' }
it { @admin_user.projects_limit.should == 50 }
end
end

View file

@ -111,42 +111,52 @@ describe Gitlab::API do
end
end
describe "GET /projects/:id/users" do
it "should return project users" do
get api("/projects/#{project.code}/users", user)
describe "GET /projects/:id/members" do
it "should return project team members" do
get api("/projects/#{project.code}/members", user)
response.status.should == 200
json_response.should be_an Array
json_response.count.should == 2
json_response.first['user']['id'].should == user.id
json_response.first['email'].should == user.email
end
end
describe "POST /projects/:id/users" do
it "should add users to project" do
expect {
post api("/projects/#{project.code}/users", user),
user_ids: {"0" => user2.id}, project_access: UsersProject::DEVELOPER
}.to change {project.users_projects.where(:project_access => UsersProject::DEVELOPER).count}.by(1)
describe "GET /projects/:id/members/:user_id" do
it "should return project team member" do
get api("/projects/#{project.code}/members/#{user.id}", user)
response.status.should == 200
json_response['email'].should == user.email
json_response['access_level'].should == UsersProject::MASTER
end
end
describe "PUT /projects/:id/users" do
it "should update users to new access role" do
describe "POST /projects/:id/members" do
it "should add user to project team" do
expect {
put api("/projects/#{project.code}/users", user),
user_ids: {"0" => user3.id}, project_access: UsersProject::MASTER
}.to change {project.users_projects.where(:project_access => UsersProject::MASTER).count}.by(1)
post api("/projects/#{project.code}/members", user), user_id: user2.id,
access_level: UsersProject::DEVELOPER
}.to change { UsersProject.count }.by(1)
response.status.should == 201
json_response['email'].should == user2.email
json_response['access_level'].should == UsersProject::DEVELOPER
end
end
describe "DELETE /projects/:id/users" do
it "should delete users from project" do
describe "PUT /projects/:id/members/:user_id" do
it "should update project team member" do
put api("/projects/#{project.code}/members/#{user3.id}", user), access_level: UsersProject::MASTER
response.status.should == 200
json_response['email'].should == user3.email
json_response['access_level'].should == UsersProject::MASTER
end
end
describe "DELETE /projects/:id/members/:user_id" do
it "should remove user from project team" do
expect {
delete api("/projects/#{project.code}/users", user),
user_ids: {"0" => user3.id}
}.to change {project.users_projects.count}.by(-1)
delete api("/projects/#{project.code}/members/#{user3.id}", user)
}.to change { UsersProject.count }.by(-1)
end
end
@ -189,6 +199,27 @@ describe Gitlab::API do
end
end
describe "GET /projects/:id/repository/commits" do
context "authorized user" do
before { project.add_access(user2, :read) }
it "should return project commits" do
get api("/projects/#{project.code}/repository/commits", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['id'].should == project.commit.id
end
end
context "unauthorized user" do
it "should not return project commits" do
get api("/projects/#{project.code}/repository/commits")
response.status.should == 401
end
end
end
describe "GET /projects/:id/snippets/:snippet_id" do
it "should return a project snippet" do
get api("/projects/#{project.code}/snippets/#{snippet.id}", user)

View file

@ -0,0 +1,39 @@
require 'spec_helper'
describe Gitlab::API do
include ApiHelpers
let(:user) { Factory :user }
describe "POST /session" do
context "when valid password" do
it "should return private token" do
post api("/session"), email: user.email, password: '123456'
response.status.should == 201
json_response['email'].should == user.email
json_response['private_token'].should == user.private_token
end
end
context "when invalid password" do
it "should return authentication error" do
post api("/session"), email: user.email, password: '123'
response.status.should == 401
json_response['email'].should be_nil
json_response['private_token'].should be_nil
end
end
context "when empty password" do
it "should return authentication error" do
post api("/session"), email: user.email
response.status.should == 401
json_response['email'].should be_nil
json_response['private_token'].should be_nil
end
end
end
end

View file

@ -1,73 +0,0 @@
require 'spec_helper'
describe Gitlab::Keys do
include ApiHelpers
let(:user) {
user = Factory.create :user
user.reset_authentication_token!
user
}
let(:key) { Factory.create :key, { user: user}}
describe "GET /keys" do
context "when unauthenticated" do
it "should return authentication error" do
get api("/keys")
response.status.should == 401
end
end
context "when authenticated" do
it "should return array of ssh keys" do
user.keys << key
user.save
get api("/keys", user)
response.status.should == 200
json_response.should be_an Array
json_response.first["title"].should == key.title
end
end
end
describe "GET /keys/:id" do
it "should returm single key" do
user.keys << key
user.save
get api("/keys/#{key.id}", user)
response.status.should == 200
json_response["title"].should == key.title
end
it "should return 404 Not Found within invalid ID" do
get api("/keys/42", user)
response.status.should == 404
end
end
describe "POST /keys" do
it "should not create invalid ssh key" do
post api("/keys", user), { title: "invalid key" }
response.status.should == 404
end
it "should create ssh key" do
key_attrs = Factory.attributes :key
expect {
post api("/keys", user), key_attrs
}.to change{ user.keys.count }.by(1)
end
end
describe "DELETE /keys/:id" do
it "should delete existed key" do
user.keys << key
user.save
expect {
delete api("/keys/#{key.id}", user)
}.to change{user.keys.count}.by(-1)
end
it "should return 404 Not Found within invalid ID" do
delete api("/keys/42", user)
response.status.should == 404
end
end
end

View file

@ -4,6 +4,7 @@ describe Gitlab::API do
include ApiHelpers
let(:user) { Factory :user }
let(:key) { Factory :key, user: user }
describe "GET /users" do
context "when unauthenticated" do
@ -38,4 +39,68 @@ describe Gitlab::API do
json_response['email'].should == user.email
end
end
describe "GET /user/keys" do
context "when unauthenticated" do
it "should return authentication error" do
get api("/user/keys")
response.status.should == 401
end
end
context "when authenticated" do
it "should return array of ssh keys" do
user.keys << key
user.save
get api("/user/keys", user)
response.status.should == 200
json_response.should be_an Array
json_response.first["title"].should == key.title
end
end
end
describe "GET /user/keys/:id" do
it "should returm single key" do
user.keys << key
user.save
get api("/user/keys/#{key.id}", user)
response.status.should == 200
json_response["title"].should == key.title
end
it "should return 404 Not Found within invalid ID" do
get api("/user/keys/42", user)
response.status.should == 404
end
end
describe "POST /user/keys" do
it "should not create invalid ssh key" do
post api("/user/keys", user), { title: "invalid key" }
response.status.should == 404
end
it "should create ssh key" do
key_attrs = Factory.attributes :key
expect {
post api("/user/keys", user), key_attrs
}.to change{ user.keys.count }.by(1)
end
end
describe "DELETE /user/keys/:id" do
it "should delete existed key" do
user.keys << key
user.save
expect {
delete api("/user/keys/#{key.id}", user)
}.to change{user.keys.count}.by(-1)
end
it "should return 404 Not Found within invalid ID" do
delete api("/user/keys/42", user)
response.status.should == 404
end
end
end