Commit c09d2336 authored by Alex Denisov's avatar Alex Denisov

Merge branch 'master' into fix_project_access_notification

parents e6ce4729 3c132f2e
v 2.9.1
- Fixed resque custom config init
v 2.9.0 v 2.9.0
- fixed inline notes bugs - fixed inline notes bugs
- refactored rspecs - refactored rspecs
...@@ -9,8 +12,10 @@ v 2.9.0 ...@@ -9,8 +12,10 @@ v 2.9.0
- scss refactoring. gitlab_bootstrap/ dir - scss refactoring. gitlab_bootstrap/ dir
- fix git push http body bigger than 112k problem - fix git push http body bigger than 112k problem
- list of labels page under issues tab - list of labels page under issues tab
- API for milestones - API for milestones, keys
- restyled buttons - restyled buttons
- OAuth
- Comment order changed
v 2.8.1 v 2.8.1
- ability to disable gravatars - ability to disable gravatars
......
...@@ -96,6 +96,7 @@ group :assets do ...@@ -96,6 +96,7 @@ group :assets do
gem "therubyracer" gem "therubyracer"
gem 'chosen-rails' gem 'chosen-rails'
gem 'jquery-atwho-rails', '0.1.6'
gem "jquery-rails", "2.0.2" gem "jquery-rails", "2.0.2"
gem "jquery-ui-rails", "0.5.0" gem "jquery-ui-rails", "0.5.0"
gem "modernizr", "2.5.3" gem "modernizr", "2.5.3"
......
...@@ -199,6 +199,7 @@ GEM ...@@ -199,6 +199,7 @@ GEM
httpauth (0.1) httpauth (0.1)
i18n (0.6.1) i18n (0.6.1)
journey (1.0.4) journey (1.0.4)
jquery-atwho-rails (0.1.6)
jquery-rails (2.0.2) jquery-rails (2.0.2)
railties (>= 3.2.0, < 5.0) railties (>= 3.2.0, < 5.0)
thor (~> 0.14) thor (~> 0.14)
...@@ -441,6 +442,7 @@ DEPENDENCIES ...@@ -441,6 +442,7 @@ DEPENDENCIES
haml-rails haml-rails
headless headless
httparty httparty
jquery-atwho-rails (= 0.1.6)
jquery-rails (= 2.0.2) jquery-rails (= 2.0.2)
jquery-ui-rails (= 0.5.0) jquery-ui-rails (= 0.5.0)
kaminari kaminari
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do
watch(%r{^spec/.+_spec\.rb$}) watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_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" } watch('spec/spec_helper.rb') { "spec" }
# Rails example # Rails example
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
//= require jquery.endless-scroll //= require jquery.endless-scroll
//= require jquery.highlight //= require jquery.highlight
//= require jquery.waitforimages //= require jquery.waitforimages
//= require jquery.atwho
//= require bootstrap //= require bootstrap
//= require modernizr //= require modernizr
//= require chosen-jquery //= require chosen-jquery
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* the top of the compiled file, but it's generally better to create a new file per style scope. * 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.all
*= require jquery.ui.aristo *= require jquery.ui.aristo
*= require jquery.atwho
*= require chosen *= require chosen
*= require_self *= require_self
*= require main *= require main
......
...@@ -185,36 +185,6 @@ span.update-author { ...@@ -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 { form {
@extend .form-horizontal; @extend .form-horizontal;
...@@ -355,41 +325,6 @@ p.time { ...@@ -355,41 +325,6 @@ p.time {
border:2px solid #ddd; 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 { .ico {
background: url("images.png") no-repeat -85px -77px; background: url("images.png") no-repeat -85px -77px;
...@@ -639,22 +574,6 @@ li.note { ...@@ -639,22 +574,6 @@ li.note {
background:#fff; 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, .supp_diff_link,
.mr_show_all_commits { .mr_show_all_commits {
......
/** LISTS **/ /** LISTS **/
ul { ul {
/** /**
* List li block element #1 * List li block element #1
* *
...@@ -18,7 +18,7 @@ ul { ...@@ -18,7 +18,7 @@ ul {
.author { color: #999; } .author { color: #999; }
p { p {
padding-top:5px; padding-top:5px;
margin:0; margin:0;
color:#222; color:#222;
img { img {
......
...@@ -143,6 +143,7 @@ $hover: #fdf5d9; ...@@ -143,6 +143,7 @@ $hover: #fdf5d9;
@import "sections/projects.scss"; @import "sections/projects.scss";
@import "sections/merge_requests.scss"; @import "sections/merge_requests.scss";
@import "sections/graph.scss"; @import "sections/graph.scss";
@import "sections/events.scss";
/** /**
* This scss file redefine chozen selectbox styles for * This scss file redefine chozen selectbox styles for
......
.commit-box { .commit-box {
@extend .main_box; @extend .main_box;
.commit-head { .commit-head {
@extend .top_box_content; @extend .top_box_content;
.commit-title { .commit-title {
...@@ -29,11 +29,11 @@ ...@@ -29,11 +29,11 @@
.sha-block { .sha-block {
text-align:right; text-align:right;
&:first-child { &:first-child {
padding-bottom:6px; padding-bottom:6px;
} }
a { a {
border-bottom: 1px solid #aaa; border-bottom: 1px solid #aaa;
margin-left: 9px; margin-left: 9px;
} }
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
} }
/** /**
* *
* COMMIT SHOw * COMMIT SHOw
* *
*/ */
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
span { span {
font-size:14px; font-size:14px;
} }
} }
...@@ -111,8 +111,8 @@ ...@@ -111,8 +111,8 @@
} }
} }
&.img_compared { &.img_compared {
img { img {
max-width:300px; max-width:300px;
} }
} }
...@@ -120,12 +120,12 @@ ...@@ -120,12 +120,12 @@
} }
.diff_file_content{ .diff_file_content{
table { table {
border:none; border:none;
margin:0px; margin:0px;
padding:0px; padding:0px;
tr { tr {
td { td {
font-size:12px; font-size:12px;
} }
} }
...@@ -145,29 +145,29 @@ ...@@ -145,29 +145,29 @@
moz-user-select: none; moz-user-select: none;
-khtml-user-select: none; -khtml-user-select: none;
user-select: none; user-select: none;
a { a {
float:left; float:left;
width:35px; width:35px;
font-weight:normal; font-weight:normal;
color:#666; color:#666;
&:hover { &:hover {
text-decoration:underline; text-decoration:underline;
} }
} }
} }
.line_content { .line_content {
white-space:pre; white-space:pre;
height:14px; height:14px;
margin:0px; margin:0px;
padding:0px; padding:0px;
border:none; border:none;
&.new { &.new {
background: #CFD; background: #CFD;
} }
&.old { &.old {
background: #FDD; background: #FDD;
} }
&.matched { &.matched {
color:#ccc; color:#ccc;
background:#fafafa; background:#fafafa;
} }
...@@ -182,32 +182,32 @@ ...@@ -182,32 +182,32 @@
/** COMMIT ROW **/ /** COMMIT ROW **/
.commit { .commit {
@extend .wll; @extend .wll;
.browse_code_link_holder { .browse_code_link_holder {
@extend .span2; @extend .span2;
float:right; float:right;
} }
.committed_ago { .committed_ago {
float:right; float:right;
@extend .cgray; @extend .cgray;
} }
code { code {
background:#FCEEC1; background:#FCEEC1;
color:$style_color; color:$style_color;
} }
.commit_short_id { .commit_short_id {
float:left; float:left;
@extend .lined; @extend .lined;
min-width:65px; min-width:65px;
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
} }
.commit-author-name { .commit-author-name {
color: #777; color: #777;
} }
} }
......
/**
* 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;
}
}
...@@ -43,7 +43,9 @@ ...@@ -43,7 +43,9 @@
padding: 8px 0; padding: 8px 0;
overflow: hidden; overflow: hidden;
display: block; display: block;
position:relative;
img {float: left; margin-right: 10px;} img {float: left; margin-right: 10px;}
img.emoji {float:none;margin:0;}
.note-author cite{font-style: italic;} .note-author cite{font-style: italic;}
p { color:$style_color; } p { color:$style_color; }
.note-author { color: $style_color;} .note-author { color: $style_color;}
...@@ -55,7 +57,9 @@ ...@@ -55,7 +57,9 @@
.delete-note { .delete-note {
display:none; display:none;
float:right; position:absolute;
right:0;
top:0;
} }
&:hover { &:hover {
......
...@@ -30,7 +30,7 @@ class Admin::UsersController < AdminController ...@@ -30,7 +30,7 @@ class Admin::UsersController < AdminController
def new 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 end
def edit def edit
...@@ -60,7 +60,7 @@ class Admin::UsersController < AdminController ...@@ -60,7 +60,7 @@ class Admin::UsersController < AdminController
def create def create
admin = params[:user].delete("admin") 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) @admin_user.admin = (admin && admin.to_i > 0)
respond_to do |format| respond_to do |format|
...@@ -86,7 +86,7 @@ class Admin::UsersController < AdminController ...@@ -86,7 +86,7 @@ class Admin::UsersController < AdminController
@admin_user.admin = (admin && admin.to_i > 0) @admin_user.admin = (admin && admin.to_i > 0)
respond_to do |format| 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.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' }
format.json { head :ok } format.json { head :ok }
else else
......
...@@ -52,6 +52,7 @@ class CommitsController < ApplicationController ...@@ -52,6 +52,7 @@ class CommitsController < ApplicationController
@commits = result[:commits] @commits = result[:commits]
@commit = result[:commit] @commit = result[:commit]
@diffs = result[:diffs] @diffs = result[:diffs]
@refs_are_same = result[:same]
@line_notes = [] @line_notes = []
@commits = CommitDecorator.decorate(@commits) @commits = CommitDecorator.decorate(@commits)
......
class HooksController < ApplicationController class HooksController < ApplicationController
before_filter :authenticate_user!
before_filter :project before_filter :project
layout "project" layout "project"
......
class IssuesController < ApplicationController class IssuesController < ApplicationController
before_filter :authenticate_user!
before_filter :project before_filter :project
before_filter :module_enabled before_filter :module_enabled
before_filter :issue, only: [:edit, :update, :destroy, :show] before_filter :issue, only: [:edit, :update, :destroy, :show]
......
class LabelsController < ApplicationController class LabelsController < ApplicationController
before_filter :authenticate_user!
before_filter :project before_filter :project
before_filter :module_enabled before_filter :module_enabled
......
class MergeRequestsController < ApplicationController class MergeRequestsController < ApplicationController
before_filter :authenticate_user!
before_filter :project before_filter :project
before_filter :module_enabled before_filter :module_enabled
before_filter :merge_request, only: [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check, :raw] before_filter :merge_request, only: [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check, :raw]
......
class MilestonesController < ApplicationController class MilestonesController < ApplicationController
before_filter :authenticate_user!
before_filter :project before_filter :project
before_filter :module_enabled before_filter :module_enabled
before_filter :milestone, only: [:edit, :update, :destroy, :show] before_filter :milestone, only: [:edit, :update, :destroy, :show]
......
class SnippetsController < ApplicationController class SnippetsController < ApplicationController
before_filter :authenticate_user!
before_filter :project before_filter :project
before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw] before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw]
layout "project" layout "project"
......
...@@ -32,7 +32,11 @@ module TreeHelper ...@@ -32,7 +32,11 @@ module TreeHelper
# #
# Returns boolean # Returns boolean
def markup?(filename) 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)) .mediawiki .rst .asciidoc .pod))
end
def gitlab_markdown?(filename)
filename.end_with?(*%w(.mdown .md .markdown))
end end
end end
...@@ -82,20 +82,24 @@ class Commit ...@@ -82,20 +82,24 @@ class Commit
end end
def compare(project, from, to) def compare(project, from, to)
first = project.commit(to.try(:strip))
last = project.commit(from.try(:strip))
result = { result = {
commits: [], commits: [],
diffs: [], 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 if first && last
commits = [first, last].sort_by(&:created_at) commits = [first, last].sort_by(&:created_at)
younger = commits.first younger = commits.first
older = commits.last 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[: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[:diffs] = project.repo.diff(younger.id, older.id) rescue []
result[:commit] = Commit.new(older) result[:commit] = Commit.new(older)
......
...@@ -132,6 +132,7 @@ class Event < ActiveRecord::Base ...@@ -132,6 +132,7 @@ class Event < ActiveRecord::Base
end end
end end
delegate :name, :email, to: :author, prefix: true, allow_nil: true delegate :name, :email, to: :author, prefix: true, allow_nil: true
delegate :title, to: :issue, prefix: true, allow_nil: true delegate :title, to: :issue, prefix: true, allow_nil: true
delegate :title, to: :merge_request, prefix: true, allow_nil: true delegate :title, to: :merge_request, prefix: true, allow_nil: true
......
...@@ -6,8 +6,9 @@ class User < ActiveRecord::Base ...@@ -6,8 +6,9 @@ class User < ActiveRecord::Base
:recoverable, :rememberable, :trackable, :validatable, :omniauthable :recoverable, :rememberable, :trackable, :validatable, :omniauthable
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, attr_accessible :email, :password, :password_confirmation, :remember_me, :bio,
:name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme, :name, :skype, :linkedin, :twitter, :dark_scheme,
:theme_id, :force_random_password, :extern_uid, :provider :theme_id, :force_random_password, :extern_uid, :provider, :as => [:default, :admin]
attr_accessible :projects_limit, :as => :admin
attr_accessor :force_random_password attr_accessor :force_random_password
......
...@@ -79,6 +79,14 @@ module Repository ...@@ -79,6 +79,14 @@ module Repository
@heads ||= repo.heads @heads ||= repo.heads
end end
def branches_names
heads.map(&:name)
end
def ref_names
[branches_names + tags].flatten
end
def tree(fcommit, path = nil) def tree(fcommit, path = nil)
fcommit = commit if fcommit == :head fcommit = commit if fcommit == :head
tree = fcommit.tree tree = fcommit.tree
......
= render "head" = render "head"
%h3 %h3.page_title
Compare View Compare View
%hr %hr
%div %div
%p %p.slead
Fill input field with commit id like Fill input field with commit id like
%code '4eedf23' %code.label_branch 4eedf23
or branch/tag name like or branch/tag name like
%code master %code.label_branch master
&amp; press compare button for commits list, code diff. and press compare button for commits list, code diff.
%br %br
...@@ -19,22 +19,24 @@ ...@@ -19,22 +19,24 @@
= text_field_tag :from, params[:from], placeholder: "master", class: "xlarge" = text_field_tag :from, params[:from], placeholder: "master", class: "xlarge"
= "..." = "..."
= text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge" = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge"
- if @refs_are_same
.alert
%span Refs are the same
.actions .actions
= submit_tag "Compare", class: "btn primary" = submit_tag "Compare", class: "btn primary wide commits-compare-btn"
- if @commits.present?
- unless @commits.empty?
%div.ui-box %div.ui-box
%h5.small Commits (#{@commits.count}) %h5.small Commits (#{@commits.count})
%ul.unstyled= render @commits %ul.unstyled= render @commits
- unless @diffs.empty? - unless @diffs.empty?
%h4 Diff %h4 Diff
= render "commits/diffs", diffs: @diffs = render "commits/diffs", diffs: @diffs
:javascript :javascript
$(function() { $(function() {
var availableTags = #{@project.heads.map(&:name).to_json}; var availableTags = #{@project.ref_names.to_json};
$("#from").autocomplete({ $("#from").autocomplete({
source: availableTags, source: availableTags,
...@@ -45,5 +47,7 @@ ...@@ -45,5 +47,7 @@
source: availableTags, source: availableTags,
minLength: 1 minLength: 1
}); });
disableButtonIfEmptyField('#to', '.commits-compare-btn');
}); });
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
%li.commit %li.commit
%p %p
= link_to commit.short_id(8), project_commit_path(project, id: commit.id), class: "commit_short_id" = 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; &ndash;
= image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16 = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
= gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding" = gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding"
- if event.allowed? - if event.allowed?
- if event.issue? %div.event-item
.event_feed - if event.issue?
= render "events/event_issue", event: event = render "events/event_issue", event: event
- elsif event.merge_request? - elsif event.merge_request?
.event_feed
= render "events/event_merge_request", event: event = render "events/event_merge_request", event: event
- elsif event.push? - elsif event.push?
.event_feed
= render "events/event_push", event: event = render "events/event_push", event: event
- elsif event.membership_changed? - elsif event.membership_changed?
.event_feed
= render "events/event_membership_changed", event: event = render "events/event_membership_changed", event: event
%span.cgray.right
= time_ago_in_words(event.created_at)
ago.
.clearfix
= image_tag gravatar_icon(event.author_email), class: "avatar" = image_tag gravatar_icon(event.author_email), class: "avatar"
%strong #{event.author_name} .event-title
%span.event_label{class: event.action_name}= event.action_name %strong.author_name #{event.author_name}
issue %span.event_label{class: event.action_name} #{event.action_name} issue
= link_to project_issue_path(event.project, event.issue) do = link_to project_issue_path(event.project, event.issue) do
%strong= truncate event.issue_title %strong= truncate event.issue_title
at at
%strong= link_to event.project.name, event.project %strong= link_to event.project.name, event.project
%span.cgray
= time_ago_in_words(event.created_at)
ago.
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.event_lp .event_lp
%div %div
= image_tag gravatar_icon(event.author_email), class: "avatar" = image_tag gravatar_icon(event.author_email), class: "avatar"
%span Your pushed to %span You pushed to
= event.ref_type = event.ref_type
= link_to project_commits_path(event.project, ref: event.ref_name) do = link_to project_commits_path(event.project, ref: event.ref_name) do
%strong= truncate(event.ref_name, length: 28) %strong= truncate(event.ref_name, length: 28)
......
= image_tag gravatar_icon(event.author_email), class: "avatar" = image_tag gravatar_icon(event.author_email), class: "avatar"
%strong #{event.author_name} .event-title
%span.event_label{class: event.action_name}= event.action_name %strong.author_name #{event.author_name}
project %span.event_label{class: event.action_name} #{event.action_name} project
%strong= link_to event.project_name, event.project %strong= link_to event.project_name, event.project
%span.cgray %span.cgray
= time_ago_in_words(event.created_at) = time_ago_in_words(event.created_at)
ago. ago.
- if event.action_name == "merged" - if event.action_name == "merged"
.event_icon= image_tag "event_mr_merged.png" .event_icon= image_tag "event_mr_merged.png"
= image_tag gravatar_icon(event.author_email), class: "avatar" = image_tag gravatar_icon(event.author_email), class: "avatar"
%strong #{event.author_name} .event-title
%span.event_label{class: event.action_name}= event.action_name %strong.author_name #{event.author_name}
merge request %span.event_label{class: event.action_name} #{event.action_name} merge request
= link_to project_merge_request_path(event.project, event.merge_request) do = link_to project_merge_request_path(event.project, event.merge_request) do
%strong= truncate event.merge_request_title %strong= truncate event.merge_request_title
at at
%strong= link_to event.project.name, event.project %strong= link_to event.project.name, event.project
%span.cgray .event-body
= time_ago_in_words(event.created_at) .event-info
ago. %span= event.merge_request.source_branch
%br &rarr;
%span= event.merge_request.source_branch %span= event.merge_request.target_branch
&rarr;
%span= event.merge_request.target_branch
%div %div
.event_icon= image_tag "event_push.png" .event_icon= image_tag "event_push.png"
= image_tag gravatar_icon(event.author_email), class: "avatar" = image_tag gravatar_icon(event.author_email), class: "avatar"
%strong #{event.author_name}
%span.event_label.pushed= event.push_action_name .event-title
= event.ref_type %strong.author_name #{event.author_name}
= link_to project_commits_path(event.project, ref: event.ref_name) do %span.event_label.pushed #{event.push_action_name} #{event.ref_type}
%strong= event.ref_name = link_to project_commits_path(event.project, ref: event.ref_name) do
at %strong= event.ref_name
%strong= link_to event.project.name, event.project at
%span.cgray %strong= link_to event.project.name, event.project
= time_ago_in_words(event.created_at)
ago.
- if event.push_with_commits? - 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 - project = event.project
%ul.unstyled.event_commits .event-body
- if event.commits_count > 3 %ul.unstyled.event_commits
- event.commits[0...2].each do |commit| - few_commits = event.commits[0...2]
= render "events/commit", commit: commit, project: project - few_commits.each do |commit|
%li
%br
\... and #{event.commits_count - 2} more commits
- else
- event.commits.each do |commit|
= render "events/commit", commit: commit, project: project = 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
%h3 API %h3.page_title API
.back_link .back_link
= link_to help_path do = link_to help_path do
&larr; to index &larr; to index
%hr %br
%ol %ul.nav.nav-tabs.log-tabs
%li.active
= link_to "README", "#README", 'data-toggle' => 'tab'
%li %li
%a{href: "#README"} README = link_to "Projects", "#projects", 'data-toggle' => 'tab'
%li %li
%a{href: "#projects"} Projects = link_to "Snippets", "#snippets", 'data-toggle' => 'tab'
%li %li
%a{href: "#snippets"} Snippets = link_to "Repositories", "#repositories", 'data-toggle' => 'tab'
%li %li
%a{href: "#users"} Users = link_to "Users", "#users", 'data-toggle' => 'tab'
%li %li
%a{href: "#issues"} Issues = link_to "Session", "#session", 'data-toggle' => 'tab'
%li %li
%a{href: "#milestones"} Milestones = link_to "Issues", "#issues", 'data-toggle' => 'tab'
%li
.file_holder#README = link_to "Milestones", "#milestones", 'data-toggle' => 'tab'
.file_title
%i.icon-file
README
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "README.md"))
%br
.file_holder#projects
.file_title
%i.icon-file
Projects
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "projects.md"))
%br .tab-content
.tab-pane.active#README
.file_holder
.file_title
%i.icon-file
README
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "README.md"))
.file_holder#snippets .tab-pane#projects
.file_title .file_holder
%i.icon-file .file_title
Projects Snippets %i.icon-file
.file_content.wiki Projects
= preserve do .file_content.wiki
= markdown File.read(Rails.root.join("doc", "api", "snippets.md")) = preserve do
= markdown File.read(Rails.root.join("doc", "api", "projects.md"))
%br .tab-pane#snippets
.file_holder
.file_title
%i.icon-file
Projects Snippets
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "snippets.md"))
.file_holder#users .tab-pane#repositories
.file_title .file_holder
%i.icon-file .file_title
Users %i.icon-file
.file_content.wiki Projects
= preserve do .file_content.wiki
= markdown File.read(Rails.root.join("doc", "api", "users.md")) = preserve do
= markdown File.read(Rails.root.join("doc", "api", "repositories.md"))
%br .tab-pane#users
.file_holder
.file_title
%i.icon-file
Users
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "users.md"))
.file_holder#issues .tab-pane#session
.file_title .file_holder
%i.icon-file .file_title
Issues %i.icon-file
.file_content.wiki Session
= preserve do .file_content.wiki
= markdown File.read(Rails.root.join("doc", "api", "issues.md")) = preserve do
= markdown File.read(Rails.root.join("doc", "api", "session.md"))
%br .tab-pane#issues
.file_holder
.file_title
%i.icon-file
Issues
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "issues.md"))
.file_holder#milestones .tab-pane#milestones
.file_title .file_holder
%i.icon-file .file_title
Milestones %i.icon-file
.file_content.wiki Milestones
= preserve do .file_content.wiki
= markdown File.read(Rails.root.join("doc", "api", "milestones.md")) = preserve do
= markdown File.read(Rails.root.join("doc", "api", "milestones.md"))
...@@ -37,3 +37,14 @@ ...@@ -37,3 +37,14 @@
= f.file_field :attachment, class: "input-file" = f.file_field :attachment, class: "input-file"
%span.hint Any file less than 10 MB %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>"
});
});
...@@ -43,7 +43,11 @@ ...@@ -43,7 +43,11 @@
%i.icon-file %i.icon-file
= content.name = content.name
.file_content.wiki .file_content.wiki
= raw GitHub::Markup.render(content.name, content.data) - if gitlab_markdown?(content.name)
= preserve do
= markdown(content.data)
- else
= raw GitHub::Markup.render(content.name, content.data)
:javascript :javascript
$(function(){ $(function(){
......
...@@ -9,7 +9,11 @@ ...@@ -9,7 +9,11 @@
= link_to "history", project_commits_path(@project, path: params[:path], ref: @ref), class: "btn very_small" = 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" = link_to "blame", blame_file_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small"
- if file.text? - if file.text?
- if markup?(name) - if gitlab_markdown?(name)
.file_content.wiki
= preserve do
= markdown(file.data)
- elsif markup?(name)
.file_content.wiki .file_content.wiki
= raw GitHub::Markup.render(name, file.data) = raw GitHub::Markup.render(name, file.data)
- else - else
......
# Custom Redis configuration
rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..' rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
rails_env = ENV['RAILS_ENV'] || 'development' rails_env = ENV['RAILS_ENV'] || 'development'
config_file = File.join(rails_root, 'config', 'resque.yml') config_file = File.join(rails_root, 'config', 'resque.yml')
...@@ -6,3 +7,25 @@ if File.exists?(config_file) ...@@ -6,3 +7,25 @@ if File.exists?(config_file)
resque_config = YAML.load_file(config_file) resque_config = YAML.load_file(config_file)
Resque.redis = resque_config[rails_env] Resque.redis = resque_config[rails_env]
end 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 = []
Resque.watch_queue(PostReceive.instance_variable_get("@queue"))
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
\ No newline at end of file
Resque::Mailer.excluded_environments = []
...@@ -6,7 +6,7 @@ working_directory app_dir ...@@ -6,7 +6,7 @@ working_directory app_dir
# worker spawn times # worker spawn times
preload_app true preload_app true
# nuke workers after 60 seconds (the default) # nuke workers after 30 seconds (60 is the default)
timeout 30 timeout 30
# listen on a Unix domain socket and/or a TCP port, # listen on a Unix domain socket and/or a TCP port,
......
...@@ -30,8 +30,9 @@ When listing resources you can pass the following parameters: ...@@ -30,8 +30,9 @@ When listing resources you can pass the following parameters:
## Contents ## Contents
+ [Users](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/users.md) + [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) + [Projects](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md)
+ [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.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) + [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md)
+ [Milestones](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/milestones.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)
## 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.
...@@ -102,7 +102,7 @@ Parameters: ...@@ -102,7 +102,7 @@ Parameters:
+ `name` (required) - new project name + `name` (required) - new project name
+ `code` (optional) - new project code, uses project name if not set + `code` (optional) - new project code, uses project name if not set
+ `path` (optional) - new project path, 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 + `default_branch` (optional) - 'master' by default
+ `issues_enabled` (optional) - enabled by default + `issues_enabled` (optional) - enabled by default
+ `wall_enabled` (optional) - enabled by default + `wall_enabled` (optional) - enabled by default
...@@ -112,66 +112,89 @@ Parameters: ...@@ -112,66 +112,89 @@ Parameters:
Will return created project with status `201 Created` on success, or `404 Not Will return created project with status `201 Created` on success, or `404 Not
found` on fail. 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: Parameters:
+ `id` (required) - The ID or code name of a project + `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 Get a project team member.
Add users to exiting project ```
GET /projects/:id/members/:user_id
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `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
}
``` ```
POST /projects/:id/users
## Add project team member
Add a user to a project team.
```
POST /projects/:id/members
``` ```
Parameters: Parameters:
+ `id` (required) - The ID or code name of a project + `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 user to add
+ `project_access` (required) - Project access level + `access_level` (required) - Project access level
Will return status `201 Created` on success, or `404 Not found` on fail. 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: Parameters:
+ `id` (required) - The ID or code name of a project + `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
+ `project_access` (required) - Project access level + `access_level` (required) - Project access level
Will return status `200 OK` on success, or `404 Not found` on fail. 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: Parameters:
+ `id` (required) - The ID or code name of a project + `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 ## Get project hooks
...@@ -216,135 +239,3 @@ Parameters: ...@@ -216,135 +239,3 @@ Parameters:
+ `hook_id` (required) - The ID of hook to delete + `hook_id` (required) - The ID of hook to delete
Will return status `200 OK` on success, or `404 Not found` on fail. 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.
## 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.
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
}
```
...@@ -88,3 +88,81 @@ GET /user ...@@ -88,3 +88,81 @@ GET /user
"theme_id": 1 "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.
...@@ -16,7 +16,7 @@ class Dashboard < Spinach::FeatureSteps ...@@ -16,7 +16,7 @@ class Dashboard < Spinach::FeatureSteps
end end
Then 'I should see last push widget' do 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" page.should have_link "Create Merge Request"
end end
......
...@@ -17,6 +17,6 @@ module Gitlab ...@@ -17,6 +17,6 @@ module Gitlab
mount Projects mount Projects
mount Issues mount Issues
mount Milestones mount Milestones
mount Keys mount Session
end end
end end
...@@ -9,6 +9,10 @@ module Gitlab ...@@ -9,6 +9,10 @@ module Gitlab
expose :id, :email, :name, :blocked, :created_at expose :id, :email, :name, :blocked, :created_at
end end
class UserLogin < UserBasic
expose :private_token
end
class Hook < Grape::Entity class Hook < Grape::Entity
expose :id, :url expose :id, :url
end end
...@@ -20,15 +24,20 @@ module Gitlab ...@@ -20,15 +24,20 @@ module Gitlab
expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at
end end
class UsersProject < Grape::Entity class ProjectMember < UserBasic
expose :user, using: Entities::UserBasic expose :project_access, :as => :access_level do |user, options|
expose :project_access options[:project].users_projects.find_by_user_id(user.id).project_access
end
end end
class RepoObject < Grape::Entity class RepoObject < Grape::Entity
expose :name, :commit expose :name, :commit
end end
class RepoCommit < Grape::Entity
expose :id, :short_id, :title, :author_name, :author_email, :created_at
end
class ProjectSnippet < Grape::Entity class ProjectSnippet < Grape::Entity
expose :id, :title, :file_name expose :id, :title, :file_name
expose :author, using: Entities::UserBasic expose :author, using: Entities::UserBasic
...@@ -36,7 +45,9 @@ module Gitlab ...@@ -36,7 +45,9 @@ module Gitlab
end end
class Milestone < Grape::Entity 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 end
class Issue < Grape::Entity class Issue < Grape::Entity
...@@ -49,10 +60,8 @@ module Gitlab ...@@ -49,10 +60,8 @@ module Gitlab
expose :closed, :updated_at, :created_at expose :closed, :updated_at, :created_at
end end
class Key < Grape::Entity class SSHKey < Grape::Entity
expose :id, expose :id, :title, :key
:title,
:key
end end
end end
end end
...@@ -61,7 +61,7 @@ module Gitlab ...@@ -61,7 +61,7 @@ module Gitlab
error!({'message' => message}, status) error!({'message' => message}, status)
end end
private private
def abilities def abilities
@abilities ||= begin @abilities ||= begin
......
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
...@@ -11,6 +11,8 @@ module Gitlab ...@@ -11,6 +11,8 @@ module Gitlab
# Example Request: # Example Request:
# GET /projects/:id/milestones # GET /projects/:id/milestones
get ":id/milestones" do get ":id/milestones" do
authorize! :read_milestone, user_project
present paginate(user_project.milestones), with: Entities::Milestone present paginate(user_project.milestones), with: Entities::Milestone
end end
...@@ -22,6 +24,8 @@ module Gitlab ...@@ -22,6 +24,8 @@ module Gitlab
# Example Request: # Example Request:
# GET /projects/:id/milestones/:milestone_id # GET /projects/:id/milestones/:milestone_id
get ":id/milestones/:milestone_id" do get ":id/milestones/:milestone_id" do
authorize! :read_milestone, user_project
@milestone = user_project.milestones.find(params[:milestone_id]) @milestone = user_project.milestones.find(params[:milestone_id])
present @milestone, with: Entities::Milestone present @milestone, with: Entities::Milestone
end end
...@@ -36,6 +40,8 @@ module Gitlab ...@@ -36,6 +40,8 @@ module Gitlab
# Example Request: # Example Request:
# POST /projects/:id/milestones # POST /projects/:id/milestones
post ":id/milestones" do post ":id/milestones" do
authorize! :admin_milestone, user_project
attrs = attributes_for_keys [:title, :description, :due_date] attrs = attributes_for_keys [:title, :description, :due_date]
@milestone = user_project.milestones.new attrs @milestone = user_project.milestones.new attrs
if @milestone.save if @milestone.save
......
...@@ -40,14 +40,14 @@ module Gitlab ...@@ -40,14 +40,14 @@ module Gitlab
post do post do
params[:code] ||= params[:name] params[:code] ||= params[:name]
params[:path] ||= params[:name] params[:path] ||= params[:name]
attrs = attributes_for_keys [:code, attrs = attributes_for_keys [:code,
:path, :path,
:name, :name,
:description, :description,
:default_branch, :default_branch,
:issues_enabled, :issues_enabled,
:wall_enabled, :wall_enabled,
:merge_requests_enabled, :merge_requests_enabled,
:wiki_enabled] :wiki_enabled]
@project = Project.create_by_user(attrs, current_user) @project = Project.create_by_user(attrs, current_user)
if @project.saved? if @project.saved?
...@@ -57,56 +57,83 @@ module Gitlab ...@@ -57,56 +57,83 @@ module Gitlab
end end
end end
# Get project users # Get a project team members
# #
# Parameters: # Parameters:
# id (required) - The ID or code name of a project # id (required) - The ID or code name of a project
# Example Request: # Example Request:
# GET /projects/:id/users # GET /projects/:id/members
get ":id/users" do get ":id/members" do
@users_projects = paginate user_project.users_projects @members = paginate user_project.users
present @users_projects, with: Entities::UsersProject present @members, with: Entities::ProjectMember, project: user_project
end end
# Add users to project with specified access level # Get a project team members
# #
# Parameters: # Parameters:
# id (required) - The ID or code name of a project # 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 user
# project_access (required) - Project access level
# Example Request: # Example Request:
# POST /projects/:id/users # GET /projects/:id/members/:user_id
post ":id/users" do get ":id/members/:user_id" do
@member = user_project.users.find params[:user_id]
present @member, with: Entities::ProjectMember, project: user_project
end
# Add a new project team member
#
# Parameters:
# id (required) - The ID or code name of a project
# user_id (required) - The ID of a user
# access_level (required) - Project access level
# Example Request:
# POST /projects/:id/members
post ":id/members" do
authorize! :admin_project, user_project authorize! :admin_project, user_project
user_project.add_users_ids_to_team(params[:user_ids].values, params[:project_access]) users_project = user_project.users_projects.new(
nil 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 end
# Update users to specified access level # Update project team member
# #
# Parameters: # Parameters:
# id (required) - The ID or code name of a project # 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
# project_access (required) - New project access level to # access_level (required) - Project access level
# Example Request: # Example Request:
# PUT /projects/:id/add_users # PUT /projects/:id/members/:user_id
put ":id/users" do put ":id/members/:user_id" do
authorize! :admin_project, user_project authorize! :admin_project, user_project
user_project.update_users_ids_to_role(params[:user_ids].values, params[:project_access]) users_project = user_project.users_projects.find_by_user_id params[:user_id]
nil
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 end
# Delete project users # Remove a team member from project
# #
# Parameters: # Parameters:
# id (required) - The ID or code name of a project # 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
# Example Request: # Example Request:
# DELETE /projects/:id/users # DELETE /projects/:id/members/:user_id
delete ":id/users" do delete ":id/members/:user_id" do
authorize! :admin_project, user_project authorize! :admin_project, user_project
user_project.delete_users_ids_from_team(params[:user_ids].values) users_project = user_project.users_projects.find_by_user_id params[:user_id]
nil users_project.destroy
end end
# Get project hooks # Get project hooks
...@@ -184,6 +211,24 @@ module Gitlab ...@@ -184,6 +211,24 @@ module Gitlab
present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject
end 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 # Get a project snippet
# #
# Parameters: # Parameters:
...@@ -207,6 +252,8 @@ module Gitlab ...@@ -207,6 +252,8 @@ module Gitlab
# Example Request: # Example Request:
# POST /projects/:id/snippets # POST /projects/:id/snippets
post ":id/snippets" do post ":id/snippets" do
authorize! :write_snippet, user_project
attrs = attributes_for_keys [:title, :file_name] attrs = attributes_for_keys [:title, :file_name]
attrs[:expires_at] = params[:lifetime] if params[:lifetime].present? attrs[:expires_at] = params[:lifetime] if params[:lifetime].present?
attrs[:content] = params[:code] if params[:code].present? attrs[:content] = params[:code] if params[:code].present?
...@@ -282,6 +329,8 @@ module Gitlab ...@@ -282,6 +329,8 @@ module Gitlab
# Example Request: # Example Request:
# GET /projects/:id/repository/commits/:sha/blob # GET /projects/:id/repository/commits/:sha/blob
get ":id/repository/commits/:sha/blob" do get ":id/repository/commits/:sha/blob" do
authorize! :download_code, user_project
ref = params[:sha] ref = params[:sha]
commit = user_project.commit ref commit = user_project.commit ref
......
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
...@@ -25,12 +25,59 @@ module Gitlab ...@@ -25,12 +25,59 @@ module Gitlab
end end
end end
# Get currently authenticated user resource :user do
# # Get currently authenticated user
# Example Request: #
# GET /user # Example Request:
get "/user" do # GET /user
present @current_user, with: Entities::User 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 end
end end
...@@ -5,7 +5,7 @@ module Gitlab ...@@ -5,7 +5,7 @@ module Gitlab
attr_accessor :time, :space attr_accessor :time, :space
attr_accessor :refs attr_accessor :refs
include ActionView::Helpers::SanitizeHelper include ActionView::Helpers::TagHelper
def self.to_graph(project) def self.to_graph(project)
@repo = project.repo @repo = project.repo
...@@ -166,7 +166,7 @@ module Gitlab ...@@ -166,7 +166,7 @@ module Gitlab
h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?
h[:id] = sha h[:id] = sha
h[:date] = date h[:date] = date
h[:message] = sanitize(Gitlab::Encode.utf8(message)) h[:message] = escape_once(Gitlab::Encode.utf8(message))
h[:login] = author.email h[:login] = author.email
h h
end end
......
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| task :add_users_to_project_teams => :environment do |t, args|
users = User.find_all_by_admin(false, :select => 'id').map(&:id) user_ids = User.where(:admin => false).pluck(:id)
admins = User.find_all_by_admin(true, :select => 'id').map(&:id) admin_ids = User.where(:admin => true).pluck(:id)
users.each do |user| Project.find_each do |project|
puts "#{user}" puts "Importing #{user_ids.size} users into #{project.code}"
end UsersProject.bulk_import(project, user_ids, UsersProject::DEVELOPER)
puts "Importing #{admin_ids.size} admins into #{project.code}"
Project.all.each do |project| UsersProject.bulk_import(project, admin_ids, UsersProject::MASTER)
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)
end end
end end
desc "Add user to as a developer to all projects" desc "Add user to as a developer to all projects"
task :add_user_to_project_teams, [:email] => :environment do |t, args| task :add_user_to_project_teams, [:email] => :environment do |t, args|
user_email = args.email user = User.find_by_email args.email
user = User.find_by_email(user_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 end
desc "Imports existing Git repos from a directory into new projects in git_base_path" desc "Imports existing Git repos from a directory into new projects in git_base_path"
task :import_projects, [:directory,:email] => :environment do |t, args| task :import_projects, [:directory,:email] => :environment do |t, args|
user_email = args.email user_email, import_directory = args.email, args.directory
import_directory = args.directory
repos_to_import = Dir.glob("#{import_directory}/*") repos_to_import = Dir.glob("#{import_directory}/*")
git_base_path = Gitlab.config.git_base_path 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| repos_to_import.each do |repo_path|
repo_name = File.basename repo_path repo_name = File.basename repo_path
clone_path = "#{git_base_path}#{repo_name}.git"
puts " Processing #{repo_name}" puts " Processing #{repo_name}"
clone_path = "#{git_base_path}#{repo_name}.git"
if Dir.exists? clone_path if Dir.exists? clone_path
if Project.find_by_code(repo_name) if Project.find_by_code(repo_name)
...@@ -38,7 +35,6 @@ task :import_projects, [:directory,:email] => :environment do |t, args| ...@@ -38,7 +35,6 @@ task :import_projects, [:directory,:email] => :environment do |t, args|
else else
failed_count += 1 failed_count += 1
end end
end end
puts "Finished importing #{imported_count} projects (skipped #{skipped_count}, failed #{failed_count})." 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) ...@@ -49,63 +45,39 @@ def clone_bare_repo_as_git(existing_path, new_path)
git_user = Gitlab.config.ssh_user git_user = Gitlab.config.ssh_user
begin begin
sh "sudo -u #{git_user} -i git clone --bare '#{existing_path}' #{new_path}" sh "sudo -u #{git_user} -i git clone --bare '#{existing_path}' #{new_path}"
true rescue Exception => msg
rescue Exception=> msg puts " ERROR: Failed to clone #{existing_path} to #{new_path}"
puts " ERROR: Faild to clone #{existing_path} to #{new_path}" puts " Make sure #{git_user} can reach #{existing_path}"
puts " Make sure #{git_user} can reach #{existing_path}" puts " Exception-MSG: #{msg}"
puts " Exception-MSG: #{msg}"
false
end end
end end
# Creats a project in Gitlag given a @project_name@ to use (for name, web url, and code # Creates a project in GitLab given a `project_name` to use
# url) and a @user_email@ that will be assigned as the owner of the project. # (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) def create_repo_project(project_name, user_email)
user = User.find_by_email(user_email) if user = User.find_by_email(user_email)
if user
# Using find_by_code since that's the most important identifer to be unique # Using find_by_code since that's the most important identifer to be unique
if Project.find_by_code(project_name) if Project.find_by_code(project_name)
puts " INFO: Project #{project_name} already exists in Gitlab, skipping." puts " INFO: Project #{project_name} already exists in Gitlab, skipping."
false
else else
project = nil project = Project.create(
if Project.find_by_code(project_name) name: project_name,
puts " ERROR: Project already exists #{project_name}" code: project_name,
return false path: project_name,
project = Project.find_by_code(project_name) owner: user,
else description: "Automatically created from 'import_projects' rake task on #{Time.now}"
project = Project.create(
name: project_name,
code: project_name,
path: project_name,
owner: user,
description: "Automatically created from Rake on #{Time.now.to_s}"
)
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? if project.valid?
true # Add user as admin for project
project.users_projects.create!(:project_access => UsersProject::MASTER, :user => user)
project.update_repository
else else
puts " ERROR: Failed to create project #{project} because #{project.errors.first}" puts " ERROR: Failed to create project #{project} because #{project.errors.first}"
false
end end
end end
else else
puts " ERROR: #{user_email} not found, skipping" puts " ERROR: user with #{user_email} not found, skipping"
false
end end
end end
...@@ -2,22 +2,20 @@ require 'active_record/fixtures' ...@@ -2,22 +2,20 @@ require 'active_record/fixtures'
namespace :gitlab do namespace :gitlab do
namespace :app do namespace :app do
# Create backup of GitLab system
# Create backup of gitlab system desc "GITLAB | Create a backup of the GitLab system"
desc "GITLAB | Create a backup of the gitlab system"
task :backup_create => :environment do task :backup_create => :environment do
Rake::Task["gitlab:app:db_dump"].invoke Rake::Task["gitlab:app:db_dump"].invoke
Rake::Task["gitlab:app:repo_dump"].invoke Rake::Task["gitlab:app:repo_dump"].invoke
Dir.chdir(Gitlab.config.backup_path) Dir.chdir(Gitlab.config.backup_path)
# saving additional informations # saving additional informations
s = Hash.new s = {}
s["db_version"] = "#{ActiveRecord::Migrator.current_version}" s[:db_version] = "#{ActiveRecord::Migrator.current_version}"
s["backup_created_at"] = "#{Time.now}" s[:backup_created_at] = "#{Time.now}"
s["gitlab_version"] = %x{git rev-parse HEAD}.gsub(/\n/,"") s[:gitlab_version] = %x{git rev-parse HEAD}.gsub(/\n/,"")
s["tar_version"] = %x{tar --version | head -1}.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.open("#{Gitlab.config.backup_path}/backup_information.yml", "w+") do |file|
file << s.to_yaml.gsub(/^---\n/,'') file << s.to_yaml.gsub(/^---\n/,'')
...@@ -32,7 +30,7 @@ namespace :gitlab do ...@@ -32,7 +30,7 @@ namespace :gitlab do
end end
# cleanup: remove tmp files # cleanup: remove tmp files
print "Deletion of tmp directories..." print "Deleting tmp directories..."
if Kernel.system("rm -rf repositories/ db/ backup_information.yml") if Kernel.system("rm -rf repositories/ db/ backup_information.yml")
puts "[DONE]".green puts "[DONE]".green
else else
...@@ -52,26 +50,23 @@ namespace :gitlab do ...@@ -52,26 +50,23 @@ namespace :gitlab do
else else
puts "[SKIPPING]".yellow puts "[SKIPPING]".yellow
end end
end end
# Restore backup of GitLab system
# Restore backup of gitlab system
desc "GITLAB | Restore a previously created backup" desc "GITLAB | Restore a previously created backup"
task :backup_restore => :environment do task :backup_restore => :environment do
Dir.chdir(Gitlab.config.backup_path) Dir.chdir(Gitlab.config.backup_path)
# check for existing backups in the backup dir # check for existing backups in the backup dir
file_list = Dir.glob("*_gitlab_backup.tar").each.map { |f| f.split(/_/).first.to_i } 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? if file_list.count > 1 && ENV["BACKUP"].nil?
puts "Found more than one backup, please specify which one you want to restore:" puts "Found more than one backup, please specify which one you want to restore:"
puts "rake gitlab:app:backup_restore BACKUP=timestamp_of_backup" puts "rake gitlab:app:backup_restore BACKUP=timestamp_of_backup"
exit 1; exit 1;
end 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) unless File.exists?(tar_file)
puts "The specified backup doesn't exist!" puts "The specified backup doesn't exist!"
...@@ -102,16 +97,14 @@ namespace :gitlab do ...@@ -102,16 +97,14 @@ namespace :gitlab do
Rake::Task["gitlab:app:repo_restore"].invoke Rake::Task["gitlab:app:repo_restore"].invoke
# cleanup: remove tmp files # cleanup: remove tmp files
print "Deletion of tmp directories..." print "Deleting tmp directories..."
if Kernel.system("rm -rf repositories/ db/ backup_information.yml") if Kernel.system("rm -rf repositories/ db/ backup_information.yml")
puts "[DONE]".green puts "[DONE]".green
else else
puts "[FAILED]".red puts "[FAILED]".red
end end
end end
################################################################################ ################################################################################
################################# invoked tasks ################################ ################################# invoked tasks ################################
...@@ -121,7 +114,7 @@ namespace :gitlab do ...@@ -121,7 +114,7 @@ namespace :gitlab do
backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") backup_path_repo = File.join(Gitlab.config.backup_path, "repositories")
FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo) FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo)
puts "Dumping repositories:" 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 << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")]
project.each do |project| project.each do |project|
print "- Dumping repository #{project.first}... " print "- Dumping repository #{project.first}... "
...@@ -136,11 +129,11 @@ namespace :gitlab do ...@@ -136,11 +129,11 @@ namespace :gitlab do
task :repo_restore => :environment do task :repo_restore => :environment do
backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") backup_path_repo = File.join(Gitlab.config.backup_path, "repositories")
puts "Restoring 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 << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")]
project.each do |project| project.each do |project|
print "- Restoring repository #{project.first}... " 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") 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 = [ permission_commands = [
"sudo chmod -R g+rwX #{Gitlab.config.git_base_path}", "sudo chmod -R g+rwX #{Gitlab.config.git_base_path}",
...@@ -157,8 +150,9 @@ namespace :gitlab do ...@@ -157,8 +150,9 @@ namespace :gitlab do
###################################### DB ###################################### ###################################### DB ######################################
task :db_dump => :environment do task :db_dump => :environment do
backup_path_db = File.join(Gitlab.config.backup_path, "db") 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:" puts "Dumping database tables:"
ActiveRecord::Base.connection.tables.each do |tbl| ActiveRecord::Base.connection.tables.each do |tbl|
print "- Dumping table #{tbl}... " print "- Dumping table #{tbl}... "
...@@ -176,9 +170,11 @@ namespace :gitlab do ...@@ -176,9 +170,11 @@ namespace :gitlab do
end end
task :db_restore=> :environment do task :db_restore=> :environment do
backup_path_db = File.join(Gitlab.config.backup_path, "db") backup_path_db = File.join(Gitlab.config.backup_path, "db")
puts "Restoring database tables:" puts "Restoring database tables:"
Rake::Task["db:reset"].invoke Rake::Task["db:reset"].invoke
Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir| Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir|
fixture_file = File.basename(dir, ".*" ) fixture_file = File.basename(dir, ".*" )
print "- Loading fixture #{fixture_file}..." print "- Loading fixture #{fixture_file}..."
......
namespace :gitlab do namespace :gitlab do
namespace :app do namespace :app do
desc "GITLAB | Enable auto merge" desc "GITLAB | Enable auto merge"
task :enable_automerge => :environment do task :enable_automerge => :environment do
Gitlab::Gitolite.new.enable_automerge Gitlab::Gitolite.new.enable_automerge
Project.find_each do |project| Project.find_each do |project|
......
namespace :gitlab do namespace :gitlab do
namespace :gitolite do namespace :gitolite do
desc "GITLAB | Rebuild each project at gitolite config" desc "GITLAB | Rebuild each project at gitolite config"
task :update_repos => :environment do task :update_repos => :environment do
puts "Starting Projects" puts "Starting Projects"
Project.find_each(:batch_size => 100) do |project| Project.find_each(:batch_size => 100) do |project|
puts puts "\n=== #{project.name}"
puts "=== #{project.name}"
project.update_repository project.update_repository
puts puts
end end
......
...@@ -4,8 +4,7 @@ namespace :gitlab do ...@@ -4,8 +4,7 @@ namespace :gitlab do
task :setup => [ task :setup => [
'db:setup', 'db:setup',
'db:seed_fu', 'db:seed_fu',
'gitlab:app:enable_automerge' 'gitlab:app:enable_automerge'
] ]
end end
end end
namespace :gitlab do namespace :gitlab do
namespace :app do namespace :app do
desc "GITLAB | Check gitlab installation status" desc "GITLAB | Check GitLab installation status"
task :status => :environment do task :status => :environment do
puts "Starting diagnostic".yellow puts "Starting diagnostics".yellow
git_base_path = Gitlab.config.git_base_path git_base_path = Gitlab.config.git_base_path
print "config/database.yml............" 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 puts "exists".green
else else
puts "missing".red puts "missing".red
return return
end end
print "config/gitlab.yml............" 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 puts "exists".green
else else
puts "missing".red puts "missing".red
return return
end end
print "#{git_base_path}............" print "#{git_base_path}............"
if File.exists?(git_base_path) if File.exists?(git_base_path)
puts "exists".green puts "exists".green
else else
puts "missing".red puts "missing".red
return return
end end
print "#{git_base_path} is writable?............" print "#{git_base_path} is writable?............"
if File.stat(git_base_path).writable? if File.stat(git_base_path).writable?
puts "YES".green puts "YES".green
else else
puts "NO".red puts "NO".red
return return
...@@ -41,16 +41,16 @@ namespace :gitlab do ...@@ -41,16 +41,16 @@ namespace :gitlab do
`git clone #{Gitlab.config.gitolite_admin_uri} /tmp/gitolite_gitlab_test` `git clone #{Gitlab.config.gitolite_admin_uri} /tmp/gitolite_gitlab_test`
FileUtils.rm_rf("/tmp/gitolite_gitlab_test") FileUtils.rm_rf("/tmp/gitolite_gitlab_test")
print "Can clone gitolite-admin?............" print "Can clone gitolite-admin?............"
puts "YES".green puts "YES".green
rescue rescue
print "Can clone gitolite-admin?............" print "Can clone gitolite-admin?............"
puts "NO".red puts "NO".red
return return
end end
print "UMASK for .gitolite.rc is 0007? ............" 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 puts "YES".green
else else
puts "NO".red puts "NO".red
return return
...@@ -69,16 +69,15 @@ namespace :gitlab do ...@@ -69,16 +69,15 @@ namespace :gitlab do
end end
end end
if Project.count > 0
if Project.count > 0
puts "Validating projects repositories:".yellow puts "Validating projects repositories:".yellow
Project.find_each(:batch_size => 100) do |project| Project.find_each(:batch_size => 100) do |project|
print "#{project.name}....." 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) unless File.exists?(hook_file)
puts "post-receive file missing".red puts "post-receive file missing".red
next return
end end
puts "post-receive file ok".green puts "post-receive file ok".green
......
...@@ -4,7 +4,6 @@ namespace :gitlab do ...@@ -4,7 +4,6 @@ namespace :gitlab do
task :write_hooks => :environment do task :write_hooks => :environment do
gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common") gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common")
gitlab_hooks_path = Rails.root.join("lib", "hooks") gitlab_hooks_path = Rails.root.join("lib", "hooks")
gitlab_hook_files = ['post-receive'] gitlab_hook_files = ['post-receive']
gitlab_hook_files.each do |file_name| gitlab_hook_files.each do |file_name|
...@@ -20,4 +19,3 @@ namespace :gitlab do ...@@ -20,4 +19,3 @@ namespace :gitlab do
end end
end end
end end
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe TreeHelper do describe TreeHelper do
describe '#markup?' 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 it "returns true for #{type} files" do
markup?("README.#{type}").should be_true markup?("README.#{type}").should be_true
end end
......
...@@ -73,4 +73,30 @@ describe User do ...@@ -73,4 +73,30 @@ describe User do
user.authentication_token.should_not be_blank user.authentication_token.should_not be_blank
end end
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 end
...@@ -111,42 +111,52 @@ describe Gitlab::API do ...@@ -111,42 +111,52 @@ describe Gitlab::API do
end end
end end
describe "GET /projects/:id/users" do describe "GET /projects/:id/members" do
it "should return project users" do it "should return project team members" do
get api("/projects/#{project.code}/users", user) get api("/projects/#{project.code}/members", user)
response.status.should == 200 response.status.should == 200
json_response.should be_an Array json_response.should be_an Array
json_response.count.should == 2 json_response.count.should == 2
json_response.first['user']['id'].should == user.id json_response.first['email'].should == user.email
end end
end end
describe "POST /projects/:id/users" do describe "GET /projects/:id/members/:user_id" do
it "should add users to project" do it "should return project team member" do
expect { get api("/projects/#{project.code}/members/#{user.id}", user)
post api("/projects/#{project.code}/users", user), response.status.should == 200
user_ids: {"0" => user2.id}, project_access: UsersProject::DEVELOPER json_response['email'].should == user.email
}.to change {project.users_projects.where(:project_access => UsersProject::DEVELOPER).count}.by(1) json_response['access_level'].should == UsersProject::MASTER
end end
end end
describe "PUT /projects/:id/users" do describe "POST /projects/:id/members" do
it "should update users to new access role" do it "should add user to project team" do
expect { expect {
put api("/projects/#{project.code}/users", user), post api("/projects/#{project.code}/members", user), user_id: user2.id,
user_ids: {"0" => user3.id}, project_access: UsersProject::MASTER access_level: UsersProject::DEVELOPER
}.to change {project.users_projects.where(:project_access => UsersProject::MASTER).count}.by(1) }.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 "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
end end
describe "DELETE /projects/:id/users" do describe "DELETE /projects/:id/members/:user_id" do
it "should delete users from project" do it "should remove user from project team" do
expect { expect {
delete api("/projects/#{project.code}/users", user), delete api("/projects/#{project.code}/members/#{user3.id}", user)
user_ids: {"0" => user3.id} }.to change { UsersProject.count }.by(-1)
}.to change {project.users_projects.count}.by(-1)
end end
end end
...@@ -189,6 +199,27 @@ describe Gitlab::API do ...@@ -189,6 +199,27 @@ describe Gitlab::API do
end end
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 describe "GET /projects/:id/snippets/:snippet_id" do
it "should return a project snippet" do it "should return a project snippet" do
get api("/projects/#{project.code}/snippets/#{snippet.id}", user) get api("/projects/#{project.code}/snippets/#{snippet.id}", user)
......
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
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
...@@ -3,7 +3,8 @@ require 'spec_helper' ...@@ -3,7 +3,8 @@ require 'spec_helper'
describe Gitlab::API do describe Gitlab::API do
include ApiHelpers include ApiHelpers
let(:user) { Factory :user } let(:user) { Factory :user }
let(:key) { Factory :key, user: user }
describe "GET /users" do describe "GET /users" do
context "when unauthenticated" do context "when unauthenticated" do
...@@ -38,4 +39,68 @@ describe Gitlab::API do ...@@ -38,4 +39,68 @@ describe Gitlab::API do
json_response['email'].should == user.email json_response['email'].should == user.email
end end
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 end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment