Merge branch 'gitlab_guange' into szzh

This commit is contained in:
huang 2015-10-28 09:28:38 +08:00
commit 1f759cbecd
153 changed files with 7158 additions and 2098 deletions

View File

@ -7,6 +7,7 @@ unless RUBY_PLATFORM =~ /w32/
end
gem 'grack', path:'./lib/grack'
gem 'gitlab', path: 'lib/gitlab-cli'
gem 'rest-client'
gem "mysql2", "= 0.3.18"
gem 'redis-rails'

View File

@ -38,7 +38,8 @@ class ApplicationController < ActionController::Base
protect_from_forgery
def handle_unverified_request
super
cookies.delete(autologin_cookie_name)
raise(ActionController::InvalidAuthenticityToken)
# cookies.delete(autologin_cookie_name)
end
before_filter :find_first_page

View File

@ -32,9 +32,9 @@ class RepositoriesController < ApplicationController
before_filter :find_project_by_project_id, :only => [:new, :create, :newrepo]
before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
before_filter :find_project_repository, :except => [:new, :create, :newcreate, :edit, :update, :destroy, :committers, :newrepo]
before_filter :find_project_repository, :except => [:new, :create, :newcreate, :edit, :update, :destroy, :committers, :newrepo,:to_gitlab]
before_filter :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue]
before_filter :authorize , :except => [:newrepo,:newcreate,:fork]
before_filter :authorize , :except => [:newrepo,:newcreate,:fork, :to_gitlab]
accept_rss_auth :revisions
# hidden repositories filter // 隐藏代码过滤器
before_filter :check_hidden_repo, :only => [:show, :stats, :revisions, :revision, :diff ]
@ -115,9 +115,6 @@ update
}
def create
if params[:repository_scm].to_s == 'Gitlab'
# add by nwb
# 增加对gitlab版本库的支持
attrs = pickup_extra_info
@repository = Repository.factory('Git')
@repository.safe_attributes = params[:repository]
@ -125,64 +122,26 @@ update
@repository.merge_extra_info(attrs[:attrs_extra])
end
@repository.project = @project
@repository.type = 'Repository::Gitlab'
@repository.url = @repository.identifier
if request.post? && @repository.save
g = ::Gitlab.client
gid = @project.owner.gid
gproject = g.create_project(@repository.identifier,
path: @repository.identifier,
description: @project.description,
wiki_enabled: false,
wall_enabled: false,
issues_enabled: false,
snippets_enabled: false,
public: false,
user_id: gid
)
@project.gpid = gproject.id
@project.save!
redirect_to settings_project_url(@project, :tab => 'repositories')
else
redirect_to settings_project_url(@project, :tab => 'repositories')
end
else # 原逻辑
##xianbo
@root_path=RepositoriesHelper::ROOT_PATH
@repository_name=User.current.login.to_s+"/"+params[:repository][:identifier]+".git"
@project_path=@root_path+"htdocs/"+@repository_name
@repository_tag=params[:repository][:upassword] || params[:repository][:password]
@repo_name=User.current.login.to_s+"_"+params[:repository][:identifier]
logger.info "htpasswd -mb "+@root_path+"htdocs/user.passwd "+@repo_name+": "+@repository_tag
logger.info "the value of create repository"+@root_path+": "+@repository_name+": "+@project_path+": "+@repo_name
attrs = pickup_extra_info
if((@repository_tag!="")&&params[:repository_scm]=="Git")
params[:repository][:url]=@project_path
end
###xianbo
@repository = Repository.factory(params[:repository_scm])
@repository.safe_attributes = params[:repository]
if attrs[:attrs_extra].keys.any?
@repository.merge_extra_info(attrs[:attrs_extra])
end
@repository.project = @project
if request.post? && @repository.save
if(params[:repository_scm]=="Git")
system "htpasswd -mb "+@root_path+"htdocs/user.passwd "+@repo_name+" "+@repository_tag
system "echo -e '"+@repo_name+"-write:"+
" "+@repo_name+"' >> "+@root_path+"htdocs/group.passwd"
system "mkdir "+@root_path+"htdocs/"+User.current.login.to_s
system "git init --bare "+@project_path
system "mv "+@project_path+"/hooks/post-update{.sample,}"
system "chmod a+x "+@project_path+"/hooks/post-update"
system "echo -e 'Allow from all \n Order Deny,Allow \n "+
"<Limit PUT POST DELETE PROPPATCH MKCOL COPY MOVE LOCK UNLOCK> \n"+
"Require group "+@repo_name+"-write \n "+
"</Limit> \n ' >> "+
@root_path+"htdocs/"+ @repository_name+"/.htaccess"
system "cd "+@project_path+" ;git update-server-info"
File.open(@project_path+"/hooks/post-update", "w+") do |f|
f.write(HOOK_TEMPLATE)
end
@repository.update_attributes(:login => User.current.login.to_s)
end
redirect_to settings_project_url(@project, :tab => 'repositories',:repository_error_message=>@repository.errors.full_messages)
else if(@repository_tag.blank?)
#render :action => 'newrepo', :layout =>'base_projects'
redirect_to settings_project_url(@project, :tab => 'repositories',:repository => "pswd_is_null",:repository_error_message=>@repository.errors.full_messages)
else
redirect_to settings_project_url(@project, :tab => 'repositories',:repository => @repository,:repository_error_message=>@repository.errors.full_messages)
end
end
end
end
@ -228,15 +187,17 @@ update
# Build a hash with repository usernames as keys and corresponding user ids as values
@repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
flash[:notice] = l(:notice_successful_update)
redirect_to settings_project_url(@project, :tab => 'repositories')
respond_to do |format|
format.html{
render :layout => "base_projects"
}
end
elsif request.get?
respond_to do |format|
format.html{
render :layout => "base_projects"
}
end
end
end
@ -247,6 +208,16 @@ update
redirect_to settings_project_url(@project, :tab => 'repositories')
end
def to_gitlab
@project = Project.find(params[:project_id])
@repository = Repository.find(params[:id])
s = Trustie::Gitlab::Sync.new
s.sync_project(@project, path: params[:repo_name], import_url: @repository.url)
@repository.type = 'Repository::Gitlab'
@repository.save
redirect_to :controller => 'repositories', :action => 'show', :id => @project.id, to: 'gitlab'
end
def show
## TODO: the below will move to filter, done.
if !User.current.member_of?(@project)
@ -256,19 +227,23 @@ update
end
end
if params[:to] == 'gitlab'
g = Gitlab.client
g.post('/session', body: {email: User.current.mail, password: User.current.hashed_password})
redirect_to "http://192.168.41.130:3000/gitlab-org/gitlab-shell/tree/master"
unless @repository.gitlab?
# redirect_to to_gitlab_project_repository_path(@project, @repository)
render :to_gitlab
return
end
#if( !User.current.member_of?(@project) || @project.hidden_repo)
@repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
# g = Gitlab.client
# project = g.project(20)
# rr = g.trees(project.id, @path)
# r = g.get ("/projects/#{@project}/repository/tree")
# :name, :path, :kind, :size, :lastrev, :changeset
@entries = @repository.entries(@path, @rev)
# @trees = g.trees(project, @path)
@changeset = @repository.find_changeset_by_name(@rev)
#@project_path_cut = RepositoriesHelper::PROJECT_PATH_CUT
#@ip = RepositoriesHelper::REPO_IP_ADDRESS
@ -278,13 +253,20 @@ update
#Modified by young
# (show_error_not_found; return) unless @entries
@changesets = @repository.latest_changesets(@path, @rev)
@changesets_count = @repository.latest_changesets(@path, @rev).count
@changesets_latest_coimmit = @changesets.first
@properties = @repository.properties(@path, @rev)
@repositories = @project.repositories
@course_tag = params[:course]
project_path_cut = RepositoriesHelper::PROJECT_PATH_CUT
ip = RepositoriesHelper::REPO_IP_ADDRESS
gitlab_address = RepositoriesHelper::REPO_GITLAB_ADDRESS
if @repository.type.to_s=="Repository::Gitlab"
@repos_url = "http://"+gitlab_address.to_s+"/"+@project.owner.to_s+"/"+@repository.identifier+"."+"git"
else
@repos_url = "http://"+@repository.login.to_s+"_"+@repository.identifier.to_s+"@"+ip.to_s+
@repository.url.slice(project_path_cut, @repository.url.length).to_s
end
if @course_tag == 1
render :action => 'show', :layout => 'base_courses'
else
@ -327,6 +309,7 @@ update
def entry
entry_and_raw(false)
render :layout => 'base_projects'
end
def entry_and_raw(is_raw)

View File

@ -25,6 +25,7 @@ module RepositoriesHelper
end
PROJECT_PATH_CUT = 40
REPO_IP_ADDRESS = Setting.host_repository
REPO_GITLAB_ADDRESS = "git.trustie.net"
def format_revision(revision)
if revision.respond_to? :format_identifier
@ -34,6 +35,10 @@ module RepositoriesHelper
end
end
def repository_creater rep
repository_creater = User.find_by_login(rep.login) unless rep.login.nil?
end
def truncate_at_line_break(text, length = 255)
if text
text.gsub(%r{^(.{#{length}}[^\n]*)\n.+$}m, '\\1...')

View File

@ -34,6 +34,7 @@ class Member < ActiveRecord::Base
after_destroy :delete_ivite_list
def role
end

View File

@ -35,8 +35,11 @@ class MemberRole < ActiveRecord::Base
!inherited_from.nil?
end
include Trustie::Gitlab::ManageMember
private
def remove_member_if_empty
if member.roles.empty?
member.destroy

View File

@ -770,7 +770,8 @@ class Project < ActiveRecord::Base
'project_type',
'dts_test',
'attachmenttype',
'enterprise_name'
'enterprise_name',
'gpid'
@ -853,6 +854,10 @@ class Project < ActiveRecord::Base
end
end
def owner
User.find(self.user_id)
end
private
def after_parent_changed(parent_was)
@ -1167,5 +1172,7 @@ class Project < ActiveRecord::Base
:forge_act_id => self.id,:forge_act_type => "ProjectCreateInfo")
fa.save!
end
end

View File

@ -57,6 +57,11 @@ class Repository < ActiveRecord::Base
safe_attributes 'url',
:if => lambda {|repository, user| repository.new_record?}
def gitlab?
self.type == 'Repository::Gitlab'
end
def repo_create_validation
unless Setting.enabled_scm.include?(self.class.name.demodulize)
errors.add(:type, :invalid)

View File

@ -0,0 +1,259 @@
#coding=utf-8
require 'redmine/scm/adapters/git_adapter'
class Repository::Gitlab < Repository
attr_protected :root_url
validates_presence_of :url
def self.human_attribute_name(attribute_key_name, *args)
attr_name = attribute_key_name.to_s
if attr_name == "url"
attr_name = "path_to_repository"
end
super(attr_name, *args)
end
def self.scm_adapter_class
Redmine::Scm::Adapters::GitlabAdapter
end
def self.scm_name
'Gitlab'
end
def commits(authors, start_date, end_date, branch='master')
scm.commits(authors, start_date, end_date,branch).map {|commit|
[commit[:author], commit[:num]]
}
end
def report_last_commit
extra_report_last_commit
end
def extra_report_last_commit
return false if extra_info.nil?
v = extra_info["extra_report_last_commit"]
return false if v.nil?
v.to_s != '0'
end
def supports_directory_revisions?
true
end
def supports_revision_graph?
true
end
def repo_log_encoding
'UTF-8'
end
# Returns the identifier for the given git changeset
def self.changeset_identifier(changeset)
changeset.scmid
end
# Returns the readable identifier for the given git changeset
def self.format_changeset_identifier(changeset)
changeset.revision[0, 8]
end
def branches
scm.branches
end
def tags
scm.tags
end
def default_branch
scm.default_branch
rescue Exception => e
logger.error "git: error during get default branch: #{e.message}"
nil
end
def find_changeset_by_name(name)
if name.present?
changesets.where(:revision => name.to_s).first ||
changesets.where('scmid LIKE ?', "#{name}%").first
end
end
def entries(path=nil, identifier=nil)
entries = scm.entries(path, identifier, :report_last_commit => extra_report_last_commit)
load_entries_changesets(entries)
entries
end
# With SCMs that have a sequential commit numbering,
# such as Subversion and Mercurial,
# Redmine is able to be clever and only fetch changesets
# going forward from the most recent one it knows about.
#
# However, Git does not have a sequential commit numbering.
#
# In order to fetch only new adding revisions,
# Redmine needs to save "heads".
#
# In Git and Mercurial, revisions are not in date order.
# Redmine Mercurial fixed issues.
# * Redmine Takes Too Long On Large Mercurial Repository
# http://www.redmine.org/issues/3449
# * Sorting for changesets might go wrong on Mercurial repos
# http://www.redmine.org/issues/3567
#
# Database revision column is text, so Redmine can not sort by revision.
# Mercurial has revision number, and revision number guarantees revision order.
# Redmine Mercurial model stored revisions ordered by database id to database.
# So, Redmine Mercurial model can use correct ordering revisions.
#
# Redmine Mercurial adapter uses "hg log -r 0:tip --limit 10"
# to get limited revisions from old to new.
# But, Git 1.7.3.4 does not support --reverse with -n or --skip.
#
# The repository can still be fully reloaded by calling #clear_changesets
# before fetching changesets (eg. for offline resync)
def fetch_changesets
scm_brs = branches
return if scm_brs.nil? || scm_brs.empty?
h1 = extra_info || {}
h = h1.dup
repo_heads = scm_brs.map{ |br| br.scmid }
h["heads"] ||= []
prev_db_heads = h["heads"].dup
if prev_db_heads.empty?
prev_db_heads += heads_from_branches_hash
end
return if prev_db_heads.sort == repo_heads.sort
h["db_consistent"] ||= {}
if changesets.count == 0
h["db_consistent"]["ordering"] = 1
merge_extra_info(h)
self.save
elsif ! h["db_consistent"].has_key?("ordering")
h["db_consistent"]["ordering"] = 0
merge_extra_info(h)
self.save
end
save_revisions(prev_db_heads, repo_heads)
end
def save_revisions(prev_db_heads, repo_heads)
h = {}
opts = {}
opts[:reverse] = true
opts[:excludes] = prev_db_heads
opts[:includes] = repo_heads
revisions = scm.revisions('', nil, nil, opts)
return if revisions.blank?
# Make the search for existing revisions in the database in a more sufficient manner
#
# Git branch is the reference to the specific revision.
# Git can *delete* remote branch and *re-push* branch.
#
# $ git push remote :branch
# $ git push remote branch
#
# After deleting branch, revisions remain in repository until "git gc".
# On git 1.7.2.3, default pruning date is 2 weeks.
# So, "git log --not deleted_branch_head_revision" return code is 0.
#
# After re-pushing branch, "git log" returns revisions which are saved in database.
# So, Redmine needs to scan revisions and database every time.
#
# This is replacing the one-after-one queries.
# Find all revisions, that are in the database, and then remove them from the revision array.
# Then later we won't need any conditions for db existence.
# Query for several revisions at once, and remove them from the revisions array, if they are there.
# Do this in chunks, to avoid eventual memory problems (in case of tens of thousands of commits).
# If there are no revisions (because the original code's algorithm filtered them),
# then this part will be stepped over.
# We make queries, just if there is any revision.
limit = 100
offset = 0
revisions_copy = revisions.clone # revisions will change
while offset < revisions_copy.size
recent_changesets_slice = changesets.find(
:all,
:conditions => [
'scmid IN (?)',
revisions_copy.slice(offset, limit).map{|x| x.scmid}
]
)
# Subtract revisions that redmine already knows about
recent_revisions = recent_changesets_slice.map{|c| c.scmid}
revisions.reject!{|r| recent_revisions.include?(r.scmid)}
offset += limit
end
revisions.each do |rev|
transaction do
# There is no search in the db for this revision, because above we ensured,
# that it's not in the db.
save_revision(rev)
end
end
h["heads"] = repo_heads.dup
merge_extra_info(h)
self.save
end
private :save_revisions
def save_revision(rev)
parents = (rev.parents || []).collect{|rp| find_changeset_by_name(rp)}.compact
changeset = Changeset.create(
:repository => self,
:revision => rev.identifier,
:scmid => rev.scmid,
:committer => rev.author,
:committed_on => rev.time,
:comments => rev.message,
:parents => parents
)
unless changeset.new_record?
rev.paths.each { |change| changeset.create_change(change) }
end
changeset
end
private :save_revision
def heads_from_branches_hash
h1 = extra_info || {}
h = h1.dup
h["branches"] ||= {}
h['branches'].map{|br, hs| hs['last_scmid']}
end
def latest_changesets(path,rev,limit=10)
revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
return [] if revisions.nil? || revisions.empty?
changesets.find(
:all,
:conditions => [
"scmid IN (?)",
revisions.map!{|c| c.scmid}
]
)
end
def clear_extra_info_of_changesets
return if extra_info.nil?
v = extra_info["extra_report_last_commit"]
write_attribute(:extra_info, nil)
h = {}
h["extra_report_last_commit"] = v
merge_extra_info(h)
self.save
end
private :clear_extra_info_of_changesets
end

View File

@ -77,6 +77,27 @@ class Role < ActiveRecord::Base
self.givable[3..5]
end
GUEST = 10
REPORTER = 20
DEVELOPER = 30
MASTER = 40
OWNER = 50
def to_gitlab_role
case self.position
when 1,2
GUEST
when 5
REPORTER
when 4
DEVELOPER
when 3
MASTER
else
GUEST
end
end
# Copies attributes from another role, arg can be an id or a Role
def copy_from(arg, options={})
return unless arg.present?

View File

@ -209,7 +209,7 @@ class User < Principal
validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
validate :validate_password_length
# validates_email_realness_of :mail
before_create :set_mail_notification
before_create :set_mail_notification, :sync_gitlab_user
before_save :update_hashed_password
before_destroy :remove_references_before_destroy
# added by fq
@ -1079,6 +1079,17 @@ class User < Principal
end
private
def sync_gitlab_user
user = self
g = Gitlab.client
u = g.get("/users?search=#{user.mail}").first
unless u
u = g.create_user(user.mail, user.password, name: user.show_name, username: user.login, confirm: "true")
self.gid = u.id
puts "create user #{user.login}"
end
end
end

View File

@ -8,7 +8,7 @@
<a href="#L<%= line_num %>" style="padding-top: 0px;"><%= line_num %></a>
</th>
<td class="line-code">
<pre style="width:880px;word-wrap: break-word; word-break: normal; "><%= line.html_safe %></pre>
<pre style="width:auto;white-space: nowrap; "><%= line.html_safe %></pre>
</td>
</tr>
<% line_num += 1 %>

View File

@ -12,7 +12,7 @@
<%= favicon %>
<%= javascript_heads %>
<%= heads_for_theme %>
<%= stylesheet_link_tag 'public', 'pleft', 'project','prettify','jquery/jquery-ui-1.9.2','header' %>
<%= stylesheet_link_tag 'public', 'pleft', 'project','prettify','jquery/jquery-ui-1.9.2','header','repository' %>
<%= javascript_include_tag 'cookie','project', 'header','prettify','select_list_move' %>
<%= call_hook :view_layouts_base_html_head %>
<!-- page specific tags -->

View File

@ -41,7 +41,7 @@
<%# --版本库被设置成私有、module中设置不显示、没有创建版本库 三种情况不显示-- %>
<% if visible_repository?(@project) %>
<div class="subNav">
<%= link_to l(:project_module_repository), {:controller => 'repositories', :action => 'show', :id => @project.id}, :class => "f14 c_blue02" %>
<%= link_to l(:project_module_repository), {:controller => 'repositories', :action => 'show', :id => @project.id, to: 'gitlab'}, :class => "f14 c_blue02" %>
<a class="subnav_num">(<%= @project.repositories.count %>)</a>
</div>
<% end %>

View File

@ -73,13 +73,6 @@
<span class="c_grey"><%= l(:text_scm_command_not_available) %></span>
<% end %>
</li>
<% unless judge_main_repository(@project) %>
<li>
<label class="label02"><%=l(:field_repository_is_default)%></label>
<%= f.check_box :is_default, :label => "", :no_label => true %></p>
</li>
<% end %>
<li >
<input type="text" style="display: none"/> <!--阻止表单自动填充 -->
<input type="password" style="display: none"/> <!--阻止表单自动填充 -->
@ -89,11 +82,6 @@
<span class="c_grey"><%=l(:text_length_between,:min=>1,:max=>254)<<l(:text_project_identifier_info) %></span>
<% end %>
</li>
<li >
<label class="label02"><span class="c_red">*</span><%=l(:label_password)%></label>
<%= f.password_field :upassword, :label=> "", :no_label => true%>
<span class="c_grey"><%= l(:label_upassword_info)%></span>
</li>
<div class="cl"></div>
</ul>
<a href="#" onclick="$('#repository-form').submit();" class="blue_btn fl ml110"><%=l(:button_save)%></a>

View File

@ -1,32 +1,13 @@
<%= link_to @repository.identifier.present? ? h(@repository.identifier) : 'root',
<div class="git_usr_title">
<span><%= link_to @repository.identifier.present? ? h(@repository.identifier) : 'root',
{:action => 'show', :id => @project,
:repository_id => @repository.identifier_param,
:path => nil, :rev => @rev },
:class=>"fl c_blue f14 fb" %>
<%
dirs = path.split('/')
if 'file' == kind
filename = dirs.pop
end
link_path = ''
dirs.each do |dir|
next if dir.blank?
link_path << '/' unless link_path.empty?
link_path << "#{dir}"
%>
/ <%= link_to h(dir), :action => 'show', :id => @project, :repository_id => @repository.identifier_param,
:path => to_path_param(link_path), :rev => @rev %>
<% end %>
<% if filename %>
/ <%= link_to h(filename),
:action => 'changes', :id => @project, :repository_id => @repository.identifier_param,
:path => to_path_param("#{link_path}/#{filename}"), :rev => @rev %>
<% end %>
<%
# @rev is revsion or Git and Mercurial branch or tag.
# For Mercurial *tip*, @rev and @changeset are nil.
rev_text = @changeset.nil? ? @rev : format_revision(@changeset)
%>
<p class="fl f14 fb c_grey02"><%= "@ #{h rev_text}" unless rev_text.blank? %></p>
:class => "repository-title-dec"
%>
/
<%=link_to @project.owner, user_path(@project.owner), :class => "repository-title-dec" %>
</span>
</div>
<% html_title(with_leading_slash(path)) -%>

View File

@ -1,17 +1,6 @@
<div class="autoscroll">
<table class="list entries" id="browser">
<thead>
<tr id="root">
<th><%= l(:field_name) %></th>
<th><%= l(:field_filesize) %></th>
<% if @repository.report_last_commit %>
<th><%= l(:label_revision) %></th>
<th><%= l(:label_age) %></th>
<th><%= l(:field_author) %></th>
<th><%= l(:field_comments) %></th>
<% end %>
</tr>
</thead>
<tbody>
<%= render :partial => 'dir_list_content' %>
</tbody>

View File

@ -7,6 +7,7 @@
<td style="padding-left: <%=18 * depth%>px;" class="<%=
@repository.report_last_commit ? "filename" : "filename_no_report" %>">
<% if entry.is_dir? %>
<%# 展开文件目录 %>
<span class="expander" onclick="scmEntryClick('<%= tr_id %>', '<%= escape_javascript(url_for(
:action => 'show',
:id => @project,
@ -17,10 +18,11 @@
:parent_id => tr_id)) %>');">&nbsp;</span>
<% end %>
<%= link_to h(ent_name),
{:action => (entry.is_dir? ? 'show' : 'changes'), :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(ent_path), :rev => @rev},
{:action => (entry.is_dir? ? 'show' : 'entry'), :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(ent_path), :rev => @rev},
:class => (entry.is_dir? ? 'icon icon-folder' : "icon icon-file #{Redmine::MimeType.css_class_of(ent_name)}")%>
</td>
<td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
<!--<td class="size"><%#= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>-->
<% if @repository.report_last_commit %>
<td class="revision"><%= link_to_revision(entry.changeset, @repository) if entry.changeset %></td>
<td class="age"><%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %></td>

View File

@ -31,12 +31,7 @@
<%= f.text_field :login, :size => 30 %>
<input type="text" hidden="hidden">
</p>
<p>
<%= f.password_field :password, :size => 30, :name => 'ignore',
:value => ((@repository.new_record? || @repository.password.blank?) ? '' : ('x'*15)),
:onfocus => "this.value=''; this.name='repository[password]';",
:onchange => "this.name='repository[password]';" %>
</p>
</div>
<!--Ended by tanxianbo-->
<p>

View File

@ -26,7 +26,6 @@ border:none
<em class="info error"><%= l(:text_scm_command_not_available) %></em>
<% end %>
</p>
<p><%= f.check_box :is_default, :label => :field_repository_is_default %></p>
<p>
<input id="googleinputcache" size="30" type="text">
@ -36,8 +35,6 @@ border:none
<%= l(:text_repository_identifier_info).html_safe %></em>
<% end %></p>
<!-- <p><%#= f.text_field :url, :size => 60, :required => true,:readonly=>true, :class=>'textbg'%></p> -->
<p><%= f.password_field :upassword, :required =>true, :label=> :field_password %>
<em class="info"><%= l(:label_upassword_info)%></em></p>
</div>
<p>
<%= submit_tag(@repository.new_record? ? l(:button_create) : l(:button_save)) %>

View File

@ -1,34 +1,33 @@
<% content_for :header_tags do %>
<%= javascript_include_tag 'repository_navigation' %>
<% end %>
<a href="javascript:void(0);" class="pic_stats fl ml20 mt3"></a>
<%= link_to l(:label_statistics),
<!--<a href="javascript:void(0);" class="pic_stats fl ml20 mt3"></a>-->
<%#= link_to l(:label_statistics),
{:action => 'stats', :id => @project, :repository_id => @repository.identifier_param},
:class => 'mt3 c_blue fl' if @repository.supports_all_revisions? %>
<div class="repositorytitle mr15">
<% content_for :header_tags do %>
<%= javascript_include_tag 'repository_navigation' %>
<% end %>
<%= form_tag({:action => controller.action_name,
<%= form_tag({:action => controller.action_name,
:id => @project,
:repository_id => @repository.identifier_param,
:path => to_path_param(@path),
:rev => nil},
{:method => :get, :id => 'revision_selector', :class => "fl c_grey02 ml5"}) do -%>
{:method => :get, :id => 'revision_selector'}) do -%>
<!-- Branches Dropdown -->
<% if !@repository.branches.nil? && @repository.branches.length > 0 -%>
| <%= l(:label_branch) %>:
<%= select_tag :branch,
options_for_select([''] + @repository.branches, @rev),
:id => 'branch' %>
<%= l(:label_branch) %>:
<%= select_tag :branch, options_for_select([''] + @repository.branches, @rev), :id => 'branch' %>
<% end -%>
<% if !@repository.tags.nil? && @repository.tags.length > 0 -%>
| <%= l(:label_tag) %>:
<%= select_tag :tag,
options_for_select([''] + @repository.tags, @rev),
:id => 'tag' %>
<%= select_tag :tag, options_for_select([''] + @repository.tags, @rev), :id => 'tag', :style=>" display:none" %>
<% end -%>
<% if @repository.supports_all_revisions? %>
| <%= l(:label_revision) %>:
<%= text_field_tag 'rev', @rev, :size => 8 %>
<%= hidden_field_tag 'rev', @rev, :size => 8 %>
<% end %>
<% end -%>
<% end -%>
</div>

View File

@ -0,0 +1,37 @@
<div class="overall-summary overall-summary-bottomless">
<div class="stats-switcher-viewport js-stats-switcher-viewport">
<div class="stats-switcher-wrapper">
<ul class="numbers-summary">
<li class="commits">
<a data-pjax="" href="/redmine/redmine/commits/0.6-stable">
<span class="octicon octicon-history"></span>
<span class="num text-emphasized">
<%=link_to @changesets.count, {:action => 'changes', :path => to_path_param(@path), :id => @project, :repository_id => @repository.identifier_param, :rev => @rev}, :class => "num text-emphasized c_blue" %>
</span>
commits
</a>
</li>
<li>
<span class="octicon image-type"></span>
<span class="num text-emphasized" style="color: #269AC9">
<%= @repository.branches.count %>
</span>
branches
</li>
<li>
<span class="octicon octicon-organization"></span>
<span class="num text-emphasized">
<%=link_to @repository.committers.count, committers_repository_path(@repository), :class => "c_blue" %>
</span>
contributors
</li>
</ul>
</div>
</div>
</div>

View File

@ -1,18 +1,20 @@
<%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
<div class="contextual">
<%= render :partial => 'navigation' %>
<div class="project_r_h">
<div class="fl"><h2 class="project_h2_repository"><%= render :partial => 'breadcrumbs', :locals => {:path => @path, :kind => 'dir', :revision => @rev} %></h2></div>
</div>
<h3><%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %></h3>
<div class="repository_con " style="line-height:1.9;">
<%= render :partial => 'navigation' %>
<div class="cl"></div>
</div>
<%= render :partial => 'link_to_functions' %>
<%= render_properties(@properties) %>
<div class="mt40">
<%= render(:partial => 'revisions',
:locals => {:project => @project, :path => @path, :revisions => @changesets, :entry => @entry }) unless @changesets.empty? %>
<div class="mt10">
<%= render(:partial => 'revisions', :locals => {:project => @project, :path => @path, :revisions => @changesets, :entry => @entry }) unless @changesets.empty? %>
</div>
<% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %>

View File

@ -1,4 +1,8 @@
<h3><%= l(:label_repository) %></h3>
<div class="project_r_h">
<div class="fl">
<h2 class="project_h2_repository"><%= render :partial => 'breadcrumbs', :locals => {:path => @path, :kind => 'dir', :revision => @rev} %></h2>
</div>
</div>
<%= simple_format(l(:text_repository_usernames_mapping)) %>

View File

@ -1,4 +1,7 @@
<h3><%= l(:label_revision) %> <%= @diff_format_revisions %> <%=h @path %></h3>
<div class="project_r_h">
<div class="fl"><h2 class="project_h2_repository"><%= render :partial => 'breadcrumbs', :locals => {:path => @path, :kind => 'dir', :revision => @rev} %></h2></div>
</div>
<h3><%= l(:label_revision_path) %> <%=h @path %></h3>
<!-- Choose view type -->
<%= form_tag({:action => 'diff', :id => @project,

View File

@ -1,15 +1,17 @@
<%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
<div class="contextual">
<div style="padding-left: 8px; padding-top: 10px;">
<div class="contextual">
<%= render :partial => 'navigation' %>
</div>
<h3><%= l(:label_revision_path) %> <%= @path %></h3>
<%= render :partial => 'link_to_functions' %>
<%= render :partial => 'common/file', :locals => {:filename => @path, :content => @content} %>
<% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %>
<% end %>
</div>
<h3><%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h3>
<%= render :partial => 'link_to_functions' %>
<%= render :partial => 'common/file', :locals => {:filename => @path, :content => @content} %>
<% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %>
<% end %>

View File

@ -1,83 +1,74 @@
<%= call_hook(:view_repositories_show_contextual, {:repository => @repository, :project => @project}) %>
<div class="project_r_h">
<h2 class="project_h2">版本库</h2>
<div class="fl"><h2 class="project_h2_repository"><%= render :partial => 'breadcrumbs', :locals => {:path => @path, :kind => 'dir', :revision => @rev} %></h2></div>
</div>
<div class="repository_con" style="line-height:1.9;">
<div class="repositorytitle" style="float:left;">
<%= render :partial => 'breadcrumbs',
:locals => {:path => @path, :kind => 'dir', :revision => @rev} %>
<%= render :partial => 'navigation' %>
<% if @entries.nil? %>
<%# 未提交代码提示 %>
<div class=" repository-url light-well">
<% if @entries.nil? && authorize_for('repositories', 'browse') %>
<div class="page-title">
该版本库还没有上传代码!
</div>
<!--contextual end-->
<div class="cl"></div>
<div class=" c_dark f14">
<p>
<% if @repository.type.to_s=="Repository::Git" %>
<%= @repos_url %>
<% else %>
<%= h @repository.url %>
<% end %>
</p>
<p class="mb10 break_word">
(<%= l(:label_all_revisions) %><%= @repositories.sort.collect { |repo|
link_to h(repo.name),
{:controller => 'repositories', :action => 'show',
:id => @project, :repository_id => repo.identifier_param, :rev => nil, :path => nil},
:class => 'repository' + (repo == @repository ? ' selected' : ''),
:class => "mb10 break_word c_orange" }.join('&nbsp|&nbsp').html_safe %>)
</p>
<% if @repository.type.to_s=="Repository::Gitlab" %>
版本库地址:<%= @repos_url %>
<% else %>
版本库地址:<%= h @repository.url %>
<% end %>
<!-- added by bai -->
<div class="fb"><a href="http://<%=Setting.host_name %>/forums/1/memos/1232" class=" c_blue ">点击查看如何提交代码</a></div>
<div class="cl"></div>
</div>
<% else %>
<%= render :partial => 'navigation' %>
<div class="fl c_grey02 mt5 mr5">克隆网址:</div>
<textarea id="copy_rep_content" class="cloneUrl mt5 fl" type="input" placeholder="http://xianbo_trustie2@repository.trustie.net/xianbo/trustie2.git"><%=@repository.type.to_s=="Repository::Gitlab" ? @repos_url.to_s.lstrip : @repository.url %></textarea>
<a href="javascript:void(0);" class="clone_btn mt5" onclick="jsCopy()"><span class="vl_copy" title="点击复制版本库地址"></span></a>
<div class="fl mt5 ml15"><a href="javascript:void(0);" class="vl_btn fb" onclick="zip()"><span class="vl_zip"></span>ZIP</a> </div>
<div class="fr mt5"><a href="javascript:void(0);" class="vl_btn fb" onclick="zip()"><span class="vl_fork"></span>Fork</a> <span href="javascript:void(0);" class="vl_btn_2 fb">0</span> </div>
<div class="cl"></div>
<div class="recordBanner mt10">
<% if @changesets && !@changesets.empty? %>
<%= image_tag(url_to_avatar(@changesets_latest_coimmit.user), :width => "25", :height => "25", :class => "fl portraitRadius mt2 ml4 mr5") %>
<span class="fl"><div class="fb fontGrey3 mr5 fl"><%=link_to @changesets_latest_coimmit.user, user_path(@changesets_latest_coimmit.user) %></div>
<div class="fl">提交于<%= time_tag(@changesets_latest_coimmit.committed_on) %></div>
<div class="commit_content_dec fl" title="<%= @changesets_latest_coimmit.comments %>"><%= @changesets_latest_coimmit.comments %></div>
</span>
<% end %>
<span class="fr mr5 "><font class="fb ml2 mr2 vl_branch mt2">
<%= @repository.branches.count %></font> 个分支
</span>
<span class="fr mr5"><font class="fb ml2 mr2 vl_commit">
<%=link_to @changesets_count, {:action => 'changes', :path => to_path_param(@path), :id => @project, :repository_id => @repository.identifier_param, :rev => @rev} %></font> 提交
</span>
</div>
<% end %>
</div>
<!--contextual end-->
<% if !@entries.nil? && authorize_for('repositories', 'browse') %>
<%# 数据统计 %>
<%#= render :partial => 'summary' %>
<%# end %>
<%= render :partial => 'dir_list' %>
<% end %>
<%= render_properties(@properties) %>
<!-- 代码修订 -->
<% if authorize_for('repositories', 'revisions') %>
<%# if @changesets && !@changesets.empty? %>
<h3>
<%= l(:label_latest_revision_plural) %>
</h3>
<%= render :partial => 'revisions',
:locals => {:project => @project, :path => @path,
:revisions => @changesets, :entry => nil} %>
<%# end %>
<%= render_properties(@properties) %>
<p style="padding-top: 10px;">
<% if authorize_for('repositories', 'revisions') %>
<% if @changesets && !@changesets.empty? %>
<% has_branches = (!@repository.branches.nil? && @repository.branches.length > 0)
sep = '' %>
<% if @repository.supports_all_revisions? && @path.blank? %>
<%= link_to l(:label_view_all_revisions), {:action => 'revisions', :id => @project,
:repository_id => @repository.identifier_param},
:class => "orange_u_btn" %>
<% sep = '|' %>
<% end %>
<% if @repository.supports_directory_revisions? && (has_branches || !@path.blank? || !@rev.blank?) %>
<%= sep %>
<%= link_to l(:label_view_revisions),
{:action => 'changes',
:path => to_path_param(@path),
:id => @project,
:repository_id => @repository.identifier_param,
:rev => @rev},
:class => "orange_u_btn" %>
<% end %>
</p>
<% if @repository.supports_all_revisions? %>
<% content_for :header_tags do %>
<%= auto_discovery_link_tag(
:atom, params.merge(
{:format => 'atom', :action => 'revisions',
:id => @project, :page => nil, :key => User.current.rss_key})) %>
<% end %>
<%= link_to l(:label_view_all_revisions_commits), :action => 'revisions', :id => @project, :repository_id => @repository.identifier_param %>
<% end %> |
<% end %>
<% end %>
<!-- added by bai -->
<p class="fb mt10"><a href="http://<%=Setting.host_name %>/forums/1/memos/1232" class=" c_blue ">点击查看如何提交代码</a></p>
<div class="cl"></div>
<a href="https://<%=Setting.host_name %>/forums/1/memos/1232" >如何提交代码</a>
<% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %>

View File

@ -0,0 +1,12 @@
<div class="repository-update-dec">
<p class="c_orange">
<%= l(:label_repository_migrate_dec) %>
</p>
<%= form_for(@repository, url: to_gitlab_project_repository_path(@project, @repository)) do |f| %>
<input type="text" name="repo_name"/>
<button type="submit">转换到新版本</button>
<% end %>
<span class="c_grey">
<%= l(:label_repository_name_dec) %>
</span>
</div>

View File

@ -69,6 +69,12 @@ module RedmineApp
config.action_view.sanitized_allowed_tags = 'div', 'p', 'span', 'img', 'embed'
config.before_initialize do
end
config.after_initialize do
end
if File.exists?(File.join(File.dirname(__FILE__), 'additional_environment.rb'))
instance_eval File.read(File.join(File.dirname(__FILE__), 'additional_environment.rb'))
end

View File

@ -0,0 +1,9 @@
Gitlab.configure do |config|
# config.endpoint = 'http://192.168.41.130:3000/trustie/api/v3' # API endpoint URL, default: ENV['GITLAB_API_ENDPOINT']
# config.private_token = 'cK15gUDwvt8EEkzwQ_63' # user's private token, default: ENV['GITLAB_API_PRIVATE_TOKEN']
config.endpoint = 'http://git.trustie.net/trustie/api/v3' # API endpoint URL, default: ENV['GITLAB_API_ENDPOINT']
config.private_token = 'fPc_gBmEiSANve8TCfxW' # user's private token, default: ENV['GITLAB_API_PRIVATE_TOKEN']
# Optional
# config.user_agent = 'Custom User Agent' # user agent, default: 'Gitlab Ruby Gem [version]'
# config.sudo = 'user' # username for sudo mode, default: nil
end

View File

@ -481,6 +481,7 @@ en:
label_attribute_plural: Attributes
label_change_status: Change status
label_history: History
label_commit_history: Commit History
label_attachment: Files
label_attachment_delete: Delete file

View File

@ -322,6 +322,7 @@ zh:
label_latest_revision_plural: 最近的修订版本
label_view_revisions: 查看修订
label_view_all_revisions: 查看所有修订
label_view_all_revisions_commits: 查看所有提交记录
#
# 项目托管平台
#
@ -441,3 +442,7 @@ zh:
field_sharing: 共享
label_title_code_review: 代码评审
label_home_non_project: 您还没有创建项目,您可以查看系统的其它项目!
# 版本库迁移
label_repository_migrate_dec: 注意Trustie版本库近期进行了一次大的改造历史版本需要转换成新的版本输入新的版本库名即可完成转换。 转换过程可能需要等待一段时间。
label_repository_name_dec: 版本库名仅小写字母a-z、数字、破折号-和下划线_可以使用长度必须在 1 到 254 个字符之间,一旦保存,标识无法修改。

View File

@ -580,6 +580,7 @@ zh:
label_change_status: 变更状态
label_history: 历史记录
label_commit_history: 历史变更记录
label_attachment: 文件
label_file_upload: 上传资料
@ -674,6 +675,7 @@ zh:
label_branch: 分支
label_tag: 标签
label_revision: 修订
label_revision_path: 当前路径
label_revision_plural: 修订
lable_revision_code_count: 代码量
label_revision_commit_count: 提交次数
@ -924,7 +926,7 @@ zh:
button_change_password: 修改密码
button_copy: 复制
button_copy_and_follow: 复制并转到新问题
button_annotate: 追溯
button_annotate: 代码定位
button_configure: 配置
button_quote: 引用
@ -987,7 +989,7 @@ zh:
text_enumeration_destroy_question: "%{count} 个对象被关联到了这个枚举值。"
text_enumeration_category_reassign_to: '将它们关联到新的枚举值:'
text_email_delivery_not_configured: "邮件参数尚未配置,因此邮件通知功能已被禁用。\n请在config/configuration.yml中配置您的SMTP服务器信息并重新启动以使其生效。"
text_repository_usernames_mapping: "选择或更新与版本库中的用户名对应的Trustie用户。\n版本库中与Trustie中的同名用户将被自动对应。"
text_repository_usernames_mapping: "选择或更新与版本库中的用户名对应的Trustie用户版本库中与Trustie中的同名用户将被自动对应。"
text_diff_truncated: '... 差别内容超过了可显示的最大行数并已被截断'
text_custom_field_possible_values_info: '每项数值一行'
text_wiki_page_destroy_question: 此页面有 %{descendants} 个子页面和下级页面。您想进行那种操作?

View File

@ -544,6 +544,7 @@ RedmineApp::Application.routes.draw do
resources :repositories, :except => [:index, :show] do
member do
get 'newrepo', :via => [:get, :post]
put 'to_gitlab'
# get 'create', :via=>[:get, :post]
end
end

View File

@ -0,0 +1,5 @@
class AddGitlabUserIdToUsers < ActiveRecord::Migration
def change
add_column :users, :gid, :integer
end
end

View File

@ -0,0 +1,5 @@
class AddGpidToProject < ActiveRecord::Migration
def change
add_column :projects, :gpid, :integer
end
end

View File

@ -527,23 +527,26 @@ ActiveRecord::Schema.define(:version => 20151022071804) do
add_index "documents", ["created_on"], :name => "index_documents_on_created_on"
add_index "documents", ["project_id"], :name => "documents_project_id"
create_table "dts", :force => true do |t|
t.string "IPLineCode"
t.string "Description"
t.string "Num"
t.string "Variable"
t.string "TraceInfo"
t.string "Method"
create_table "dts", :primary_key => "Num", :force => true do |t|
t.string "Defect", :limit => 50
t.string "Category", :limit => 50
t.string "File"
t.string "IPLine"
t.string "Review"
t.string "Category"
t.string "Defect"
t.string "PreConditions"
t.string "StartLine"
t.string "Method"
t.string "Module", :limit => 20
t.string "Variable", :limit => 50
t.integer "StartLine"
t.integer "IPLine"
t.string "IPLineCode", :limit => 200
t.string "Judge", :limit => 15
t.integer "Review", :limit => 1
t.string "Description"
t.text "PreConditions", :limit => 2147483647
t.text "TraceInfo", :limit => 2147483647
t.text "Code", :limit => 2147483647
t.integer "project_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "id", :null => false
end
create_table "enabled_modules", :force => true do |t|
@ -812,16 +815,6 @@ ActiveRecord::Schema.define(:version => 20151022071804) do
add_index "journal_details", ["journal_id"], :name => "journal_details_journal_id"
create_table "journal_details_copy", :force => true do |t|
t.integer "journal_id", :default => 0, :null => false
t.string "property", :limit => 30, :default => "", :null => false
t.string "prop_key", :limit => 30, :default => "", :null => false
t.text "old_value"
t.text "value"
end
add_index "journal_details_copy", ["journal_id"], :name => "journal_details_journal_id"
create_table "journal_replies", :id => false, :force => true do |t|
t.integer "journal_id"
t.integer "user_id"
@ -1192,6 +1185,7 @@ ActiveRecord::Schema.define(:version => 20151022071804) do
t.string "enterprise_name"
t.integer "organization_id"
t.integer "project_new_type"
t.integer "gpid"
end
add_index "projects", ["lft"], :name => "index_projects_on_lft"
@ -1608,6 +1602,7 @@ ActiveRecord::Schema.define(:version => 20151022071804) do
t.string "identity_url"
t.string "mail_notification", :default => "", :null => false
t.string "salt", :limit => 64
t.integer "gid"
end
add_index "users", ["auth_source_id"], :name => "index_users_on_auth_source_id"

4
lib/gitlab-cli/Gemfile Normal file
View File

@ -0,0 +1,4 @@
source 'https://rubygems.org'
# Specify your gem's dependencies in gitlab.gemspec
gemspec

View File

@ -0,0 +1,24 @@
Copyright (c) 2012-2014 Nihad Abbasov <mail@narkoz.me>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

121
lib/gitlab-cli/README.md Normal file
View File

@ -0,0 +1,121 @@
# Gitlab
[![Build Status](https://travis-ci.org/NARKOZ/gitlab.png)](http://travis-ci.org/NARKOZ/gitlab)
[website](http://narkoz.github.io/gitlab) |
[documentation](http://rubydoc.info/gems/gitlab/frames)
Gitlab is a Ruby wrapper and CLI for the [GitLab API](https://github.com/gitlabhq/gitlabhq/tree/master/doc/api#gitlab-api).
## Installation
Install it from rubygems:
```sh
gem install gitlab
```
Or add to a Gemfile:
```ruby
gem 'gitlab'
# gem 'gitlab', :git => 'git://github.com/NARKOZ/gitlab.git'
```
## Usage
Configuration example:
```ruby
Gitlab.configure do |config|
config.endpoint = 'https://example.net/api/v3' # API endpoint URL, default: ENV['GITLAB_API_ENDPOINT']
config.private_token = 'qEsq1pt6HJPaNciie3MG' # user's private token, default: ENV['GITLAB_API_PRIVATE_TOKEN']
# Optional
# config.user_agent = 'Custom User Agent' # user agent, default: 'Gitlab Ruby Gem [version]'
# config.sudo = 'user' # username for sudo mode, default: nil
end
```
(Note: If you are using Gitlab.com's hosted service, your endpoint will be `https://gitlab.com/api/v3`)
Usage examples:
```ruby
# set an API endpoint
Gitlab.endpoint = 'http://example.net/api/v3'
# => "http://example.net/api/v3"
# set a user private token
Gitlab.private_token = 'qEsq1pt6HJPaNciie3MG'
# => "qEsq1pt6HJPaNciie3MG"
# list projects
Gitlab.projects(:per_page => 5)
# => [#<Gitlab::ObjectifiedHash:0x000000023326e0 @data={"id"=>1, "code"=>"brute", "name"=>"Brute", "description"=>nil, "path"=>"brute", "default_branch"=>nil, "owner"=>#<Gitlab::ObjectifiedHash:0x00000002331600 @data={"id"=>1, "email"=>"john@example.com", "name"=>"John Smith", "blocked"=>false, "created_at"=>"2012-09-17T09:41:56Z"}>, "private"=>true, "issues_enabled"=>true, "merge_requests_enabled"=>true, "wall_enabled"=>true, "wiki_enabled"=>true, "created_at"=>"2012-09-17T09:41:56Z"}>, #<Gitlab::ObjectifiedHash:0x000000023450d8 @data={"id"=>2, "code"=>"mozart", "name"=>"Mozart", "description"=>nil, "path"=>"mozart", "default_branch"=>nil, "owner"=>#<Gitlab::ObjectifiedHash:0x00000002344ca0 @data={"id"=>1, "email"=>"john@example.com", "name"=>"John Smith", "blocked"=>false, "created_at"=>"2012-09-17T09:41:56Z"}>, "private"=>true, "issues_enabled"=>true, "merge_requests_enabled"=>true, "wall_enabled"=>true, "wiki_enabled"=>true, "created_at"=>"2012-09-17T09:41:57Z"}>, #<Gitlab::ObjectifiedHash:0x00000002344958 @data={"id"=>3, "code"=>"gitlab", "name"=>"Gitlab", "description"=>nil, "path"=>"gitlab", "default_branch"=>nil, "owner"=>#<Gitlab::ObjectifiedHash:0x000000023447a0 @data={"id"=>1, "email"=>"john@example.com", "name"=>"John Smith", "blocked"=>false, "created_at"=>"2012-09-17T09:41:56Z"}>, "private"=>true, "issues_enabled"=>true, "merge_requests_enabled"=>true, "wall_enabled"=>true, "wiki_enabled"=>true, "created_at"=>"2012-09-17T09:41:58Z"}>]
# initialize a new client
g = Gitlab.client(:endpoint => 'https://api.example.com', :private_token => 'qEsq1pt6HJPaNciie3MG')
# => #<Gitlab::Client:0x00000001e62408 @endpoint="https://api.example.com", @private_token="qEsq1pt6HJPaNciie3MG", @user_agent="Gitlab Ruby Gem 2.0.0">
# get a user
user = g.user
# => #<Gitlab::ObjectifiedHash:0x00000002217990 @data={"id"=>1, "email"=>"john@example.com", "name"=>"John Smith", "bio"=>nil, "skype"=>"", "linkedin"=>"", "twitter"=>"john", "dark_scheme"=>false, "theme_id"=>1, "blocked"=>false, "created_at"=>"2012-09-17T09:41:56Z"}>
# get a user's email
user.email
# => "john@example.com"
# set a sudo mode to perform API calls as another user
Gitlab.sudo = 'other_user'
# => "other_user"
# disable a sudo mode
Gitlab.sudo = nil
# => nil
```
For more information, refer to [documentation](http://rubydoc.info/gems/gitlab/frames).
## CLI
Usage examples:
```sh
# list users
gitlab users
# get current user
gitlab user
# get a user
gitlab user 2
# filter output
gitlab user --only=id,username
gitlab user --except=email,bio
```
## CLI Shell
Usage examples:
```sh
# start shell session
gitlab shell
# list available commands
gitlab> help
# list groups
gitlab> groups
# protect a branch
gitlab> protect_branch 1 master
```
For more information, refer to [website](http://narkoz.github.io/gitlab).
## License
Released under the BSD 2-clause license. See LICENSE.txt for details.

9
lib/gitlab-cli/Rakefile Normal file
View File

@ -0,0 +1,9 @@
require "bundler/gem_tasks"
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec) do |spec|
spec.pattern = FileList['spec/**/*_spec.rb']
spec.rspec_opts = ['--color', '--format d']
end
task :default => :spec

View File

@ -0,0 +1,7 @@
#!/usr/bin/env ruby
$:.unshift File.expand_path('../../lib', __FILE__)
require 'gitlab/cli'
Gitlab::CLI.start(ARGV)

View File

@ -0,0 +1,26 @@
# -*- encoding: utf-8 -*-
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'gitlab/version'
Gem::Specification.new do |gem|
gem.name = "gitlab"
gem.version = Gitlab::VERSION
gem.authors = ["Nihad Abbasov"]
gem.email = ["mail@narkoz.me"]
gem.description = %q{Ruby client and CLI for GitLab API}
gem.summary = %q{A Ruby wrapper and CLI for the GitLab API}
gem.homepage = "https://github.com/narkoz/gitlab"
gem.files = `git ls-files`.split($/)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.require_paths = ["lib"]
gem.add_runtime_dependency 'httparty'
gem.add_runtime_dependency 'terminal-table'
gem.add_development_dependency 'rake'
gem.add_development_dependency 'rspec'
gem.add_development_dependency 'webmock'
end

View File

@ -0,0 +1,37 @@
require 'gitlab/version'
require 'gitlab/objectified_hash'
require 'gitlab/configuration'
require 'gitlab/error'
require 'gitlab/request'
require 'gitlab/api'
require 'gitlab/client'
module Gitlab
extend Configuration
# Alias for Gitlab::Client.new
#
# @return [Gitlab::Client]
def self.client(options={})
Gitlab::Client.new(options)
end
# Delegate to Gitlab::Client
def self.method_missing(method, *args, &block)
return super unless client.respond_to?(method)
client.send(method, *args, &block)
end
# Delegate to Gitlab::Client
def self.respond_to?(method)
return client.respond_to?(method) || super
end
# Returns an unsorted array of available client methods.
#
# @return [Array<Symbol>]
def self.actions
hidden = /endpoint|private_token|user_agent|sudo|get|post|put|\Adelete\z|validate|set_request_defaults/
(Gitlab::Client.instance_methods - Object.methods).reject {|e| e[hidden]}
end
end

View File

@ -0,0 +1,17 @@
module Gitlab
# @private
class API < Request
# @private
attr_accessor(*Configuration::VALID_OPTIONS_KEYS)
# Creates a new API.
# @raise [Error:MissingCredentials]
def initialize(options={})
options = Gitlab.options.merge(options)
Configuration::VALID_OPTIONS_KEYS.each do |key|
send("#{key}=", options[key])
end
set_request_defaults @endpoint, @private_token, @sudo
end
end
end

View File

@ -0,0 +1,47 @@
require 'gitlab'
require 'terminal-table/import'
require_relative 'cli_helpers'
require_relative 'shell'
class Gitlab::CLI
extend Helpers
def self.start(args)
command = args.shift.strip rescue 'help'
run(command, args)
end
def self.run(cmd, args=[])
case cmd
when 'help'
puts actions_table
when 'info'
endpoint = Gitlab.endpoint ? Gitlab.endpoint : 'not set'
private_token = Gitlab.private_token ? Gitlab.private_token : 'not set'
puts "Gitlab endpoint is #{endpoint}"
puts "Gitlab private token is #{private_token}"
puts "Ruby Version is #{RUBY_VERSION}"
puts "Gitlab Ruby Gem #{Gitlab::VERSION}"
when '-v', '--version'
puts "Gitlab Ruby Gem #{Gitlab::VERSION}"
when 'shell'
Gitlab::Shell.start
else
unless valid_command?(cmd)
puts "Unknown command. Run `gitlab help` for a list of available commands."
exit(1)
end
if args.any? && (args.last.start_with?('--only=') || args.last.start_with?('--except='))
command_args = args[0..-2]
else
command_args = args
end
confirm_command(cmd)
data = gitlab_helper(cmd, command_args) { exit(1) }
output_table(cmd, args, data)
end
end
end

View File

@ -0,0 +1,175 @@
class Gitlab::CLI
# Defines methods related to CLI output and formatting.
module Helpers
extend self
# Returns filtered required fields.
#
# @return [Array]
def required_fields(args)
if args.any? && args.last.start_with?('--only=')
args.last.gsub('--only=', '').split(',')
else
[]
end
end
# Returns filtered excluded fields.
#
# @return [Array]
def excluded_fields(args)
if args.any? && args.last.start_with?('--except=')
args.last.gsub('--except=', '').split(',')
else
[]
end
end
# Confirms command is valid.
#
# @return [Boolean]
def valid_command?(cmd)
command = cmd.is_a?(Symbol) ? cmd : cmd.to_sym
Gitlab.actions.include?(command)
end
# Confirms command with a desctructive action.
#
# @return [String]
def confirm_command(cmd)
if cmd.start_with?('remove_') || cmd.start_with?('delete_')
puts "Are you sure? (y/n)"
if %w(y yes).include?($stdin.gets.to_s.strip.downcase)
puts 'Proceeding..'
else
puts 'Command aborted.'
exit(1)
end
end
end
# Table with available commands.
#
# @return [String]
def actions_table
client = Gitlab::Client.new(endpoint: '')
actions = Gitlab.actions
methods = []
actions.each do |action|
methods << {
name: action,
owner: client.method(action).owner.to_s.gsub('Gitlab::Client::', '')
}
end
owners = methods.map {|m| m[:owner]}.uniq.sort
methods_c = methods.group_by {|m| m[:owner]}
methods_c = methods_c.map {|_, v| [_, v.sort_by {|hv| hv[:name]}] }
methods_c = Hash[methods_c.sort_by(&:first).map {|k, v| [k, v]}]
max_column_length = methods_c.values.max_by(&:size).size
rows = max_column_length.times.map do |i|
methods_c.keys.map do |key|
methods_c[key][i] ? methods_c[key][i][:name] : ''
end
end
table do |t|
t.title = "Available commands (#{actions.size} total)"
t.headings = owners
rows.each do |row|
t.add_row row
end
end
end
# Decides which table to use.
#
# @return [String]
def output_table(cmd, args, data)
case data
when Gitlab::ObjectifiedHash
puts single_record_table(data, cmd, args)
when Array
puts multiple_record_table(data, cmd, args)
else
puts data.inspect
end
end
# Table for a single record.
#
# @return [String]
def single_record_table(data, cmd, args)
hash = data.to_h
keys = hash.keys.sort {|x, y| x.to_s <=> y.to_s }
keys = keys & required_fields(args) if required_fields(args).any?
keys = keys - excluded_fields(args)
table do |t|
t.title = "Gitlab.#{cmd} #{args.join(', ')}"
keys.each_with_index do |key, index|
case value = hash[key]
when Hash
value = 'Hash'
when nil
value = 'null'
end
t.add_row [key, value]
t.add_separator unless keys.size - 1 == index
end
end
end
# Table for multiple records.
#
# @return [String]
def multiple_record_table(data, cmd, args)
return 'No data' if data.empty?
arr = data.map(&:to_h)
keys = arr.first.keys.sort {|x, y| x.to_s <=> y.to_s }
keys = keys & required_fields(args) if required_fields(args).any?
keys = keys - excluded_fields(args)
table do |t|
t.title = "Gitlab.#{cmd} #{args.join(', ')}"
t.headings = keys
arr.each_with_index do |hash, index|
values = []
keys.each do |key|
case value = hash[key]
when Hash
value = 'Hash'
when nil
value = 'null'
end
values << value
end
t.add_row values
t.add_separator unless arr.size - 1 == index
end
end
end
# Helper function to call Gitlab commands with args.
def gitlab_helper(cmd, args=[])
begin
data = args.any? ? Gitlab.send(cmd, *args) : Gitlab.send(cmd)
rescue => e
puts e.message
yield if block_given?
end
data
end
end
end

View File

@ -0,0 +1,18 @@
module Gitlab
# Wrapper for the Gitlab REST API.
class Client < API
Dir[File.expand_path('../client/*.rb', __FILE__)].each {|f| require f}
include Branches
include Groups
include Issues
include MergeRequests
include Milestones
include Notes
include Projects
include Repositories
include Snippets
include SystemHooks
include Users
end
end

View File

@ -0,0 +1,79 @@
class Gitlab::Client
# Defines methods related to repositories.
module Branches
# Gets a list of project repositiory branches.
#
# @example
# Gitlab.branches(42)
#
# @param [Integer] project The ID of a project.
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def branches(project, options={})
get("/projects/#{project}/repository/branches", :query => options)
end
alias_method :repo_branches, :branches
# Gets information about a repository branch.
#
# @example
# Gitlab.branch(3, 'api')
# Gitlab.repo_branch(5, 'master')
#
# @param [Integer] project The ID of a project.
# @param [String] branch The name of the branch.
# @return [Gitlab::ObjectifiedHash]
def branch(project, branch)
get("/projects/#{project}/repository/branches/#{branch}")
end
alias_method :repo_branch, :branch
# Protects a repository branch.
#
# @example
# Gitlab.protect_branch(3, 'api')
# Gitlab.repo_protect_branch(5, 'master')
#
# @param [Integer] project The ID of a project.
# @param [String] branch The name of the branch.
# @return [Gitlab::ObjectifiedHash]
def protect_branch(project, branch)
put("/projects/#{project}/repository/branches/#{branch}/protect")
end
alias_method :repo_protect_branch, :protect_branch
# Unprotects a repository branch.
#
# @example
# Gitlab.unprotect_branch(3, 'api')
# Gitlab.repo_unprotect_branch(5, 'master')
#
# @param [Integer] project The ID of a project.
# @param [String] branch The name of the branch.
# @return [Gitlab::ObjectifiedHash]
def unprotect_branch(project, branch)
put("/projects/#{project}/repository/branches/#{branch}/unprotect")
end
alias_method :repo_unprotect_branch, :unprotect_branch
# Creates a repository branch. Requires Gitlab >= 6.8.x
#
# @example
# Gitlab.create_branch(3, 'api')
# Gitlab.repo_create_branch(5, 'master')
#
# @param [Integer] project The ID of a project.
# @param [String] branch The name of the new branch.
# @param [String] ref Create branch from commit sha or existing branch
# @return [Gitlab::ObjectifiedHash]
def create_branch(project, branch, ref)
post("/projects/#{project}/repository/branches",:body => {:branch_name => branch, :ref => ref})
end
alias_method :repo_create_branch, :create_branch
end
end

View File

@ -0,0 +1,88 @@
class Gitlab::Client
# Defines methods related to groups.
module Groups
# Gets a list of groups.
#
# @example
# Gitlab.groups
# Gitlab.groups(:per_page => 40)
#
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def groups(options={})
get("/groups", :query => options)
end
# Gets a single group.
#
# @example
# Gitlab.group(42)
#
# @param [Integer] id The ID of a group.
# @return [Gitlab::ObjectifiedHash]
def group(id)
get("/groups/#{id}")
end
# Creates a new group.
#
# @param [String] name The name of a group.
# @param [String] path The path of a group.
# @return [Gitlab::ObjectifiedHash] Information about created group.
def create_group(name, path)
body = {:name => name, :path => path}
post("/groups", :body => body)
end
# Get a list of group members.
#
# @example
# Gitlab.group_members(1)
# Gitlab.group_members(1, :per_page => 40)
#
# @param [Integer] id The ID of a group.
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def group_members(id, options={})
get("/groups/#{id}/members", :query => options)
end
# Adds a user to group.
#
# @example
# Gitlab.add_group_member(1, 2, 40)
#
# @param [Integer] team_id The group id to add a member to.
# @param [Integer] user_id The user id of the user to add to the team.
# @param [Integer] access_level Project access level.
# @return [Gitlab::ObjectifiedHash] Information about added team member.
def add_group_member(team_id, user_id, access_level)
post("/groups/#{team_id}/members", :body => {:user_id => user_id, :access_level => access_level})
end
# Removes user from user group.
#
# @example
# Gitlab.remove_group_member(1, 2)
#
# @param [Integer] team_id The group ID.
# @param [Integer] user_id The ID of a user.
# @return [Gitlab::ObjectifiedHash] Information about removed team member.
def remove_group_member(team_id, user_id)
delete("/groups/#{team_id}/members/#{user_id}")
end
# Transfers a project to a group
#
# @param [Integer] id The ID of a group.
# @param [Integer] project_id The ID of a project.
def transfer_project_to_group(id, project_id)
body = {:id => id, :project_id => project_id}
post("/groups/#{id}/projects/#{project_id}", :body => body)
end
end
end

View File

@ -0,0 +1,92 @@
class Gitlab::Client
# Defines methods related to issues.
module Issues
# Gets a list of user's issues.
# Will return a list of project's issues if project ID passed.
#
# @example
# Gitlab.issues
# Gitlab.issues(5)
# Gitlab.issues(:per_page => 40)
#
# @param [Integer] project The ID of a project.
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def issues(project=nil, options={})
if project.to_i.zero?
get("/issues", :query => options)
else
get("/projects/#{project}/issues", :query => options)
end
end
# Gets a single issue.
#
# @example
# Gitlab.issue(5, 42)
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of an issue.
# @return [Gitlab::ObjectifiedHash]
def issue(project, id)
get("/projects/#{project}/issues/#{id}")
end
# Creates a new issue.
#
# @param [Integer] project The ID of a project.
# @param [String] title The title of an issue.
# @param [Hash] options A customizable set of options.
# @option options [String] :description The description of an issue.
# @option options [Integer] :assignee_id The ID of a user to assign issue.
# @option options [Integer] :milestone_id The ID of a milestone to assign issue.
# @option options [String] :labels Comma-separated label names for an issue.
# @return [Gitlab::ObjectifiedHash] Information about created issue.
def create_issue(project, title, options={})
body = {:title => title}.merge(options)
post("/projects/#{project}/issues", :body => body)
end
# Updates an issue.
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of an issue.
# @param [Hash] options A customizable set of options.
# @option options [String] :title The title of an issue.
# @option options [String] :description The description of an issue.
# @option options [Integer] :assignee_id The ID of a user to assign issue.
# @option options [Integer] :milestone_id The ID of a milestone to assign issue.
# @option options [String] :labels Comma-separated label names for an issue.
# @option options [String] :state_event The state event of an issue ('close' or 'reopen').
# @return [Gitlab::ObjectifiedHash] Information about updated issue.
def edit_issue(project, id, options={})
put("/projects/#{project}/issues/#{id}", :body => options)
end
# Closes an issue.
#
# @example
# Gitlab.close_issue(3, 42)
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of an issue.
# @return [Gitlab::ObjectifiedHash] Information about closed issue.
def close_issue(project, id)
put("/projects/#{project}/issues/#{id}", :body => {:state_event => 'close'})
end
# Reopens an issue.
#
# @example
# Gitlab.reopen_issue(3, 42)
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of an issue.
# @return [Gitlab::ObjectifiedHash] Information about reopened issue.
def reopen_issue(project, id)
put("/projects/#{project}/issues/#{id}", :body => {:state_event => 'reopen'})
end
end
end

View File

@ -0,0 +1,107 @@
class Gitlab::Client
# Defines methods related to merge requests.
module MergeRequests
# Gets a list of project merge requests.
#
# @example
# Gitlab.merge_requests(5)
# Gitlab.merge_requests(:per_page => 40)
#
# @param [Integer] project The ID of a project.
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def merge_requests(project, options={})
get("/projects/#{project}/merge_requests", :query => options)
end
# Gets a single merge request.
#
# @example
# Gitlab.merge_request(5, 36)
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of a merge request.
# @return <Gitlab::ObjectifiedHash]
def merge_request(project, id)
get("/projects/#{project}/merge_request/#{id}")
end
# Creates a merge request.
#
# @example
# Gitlab.create_merge_request(5, 'New merge request',
# :source_branch => 'source_branch', :target_branch => 'target_branch')
# Gitlab.create_merge_request(5, 'New merge request',
# :source_branch => 'source_branch', :target_branch => 'target_branch', :assignee_id => 42)
#
# @param [Integer] project The ID of a project.
# @param [String] title The title of a merge request.
# @param [Hash] options A customizable set of options.
# @option options [String] :source_branch (required) The source branch name.
# @option options [String] :target_branch (required) The target branch name.
# @option options [Integer] :assignee_id (optional) The ID of a user to assign merge request.
# @return [Gitlab::ObjectifiedHash] Information about created merge request.
def create_merge_request(project, title, options={})
check_attributes!(options, [:source_branch, :target_branch])
body = {:title => title}.merge(options)
post("/projects/#{project}/merge_requests", :body => body)
end
# Updates a merge request.
#
# @example
# Gitlab.update_merge_request(5, 42, :title => 'New title')
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of a merge request.
# @param [Hash] options A customizable set of options.
# @option options [String] :title The title of a merge request.
# @option options [String] :source_branch The source branch name.
# @option options [String] :target_branch The target branch name.
# @option options [Integer] :assignee_id The ID of a user to assign merge request.
# @option options [String] :state_event New state (close|reopen|merge).
# @return [Gitlab::ObjectifiedHash] Information about updated merge request.
def update_merge_request(project, id, options={})
put("/projects/#{project}/merge_request/#{id}", :body => options)
end
# Adds a comment to a merge request.
#
# @example
# Gitlab.create_merge_request_comment(5, 1, "Awesome merge!")
# Gitlab.create_merge_request_comment('gitlab', 1, "Awesome merge!")
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of a merge request.
# @param [String] note The content of a comment.
# @return [Gitlab::ObjectifiedHash] Information about created merge request comment.
def create_merge_request_comment(project, id, note)
post("/projects/#{project}/merge_request/#{id}/comments", :body => {:note => note})
end
# Gets the comments on a merge request.
#
# @example
# Gitlab.merge_request_comments(5, 1)
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of a merge request.
# @return [Gitlab::ObjectifiedHash] The merge request's comments.
def merge_request_comments(project, id)
get("/projects/#{project}/merge_request/#{id}/comments")
end
private
def check_attributes!(options, attrs)
attrs.each do |attr|
unless options.has_key?(attr) || options.has_key?(attr.to_s)
raise Gitlab::Error::MissingAttributes.new("Missing '#{attr}' parameter")
end
end
end
end
end

View File

@ -0,0 +1,57 @@
class Gitlab::Client
# Defines methods related to milestones.
module Milestones
# Gets a list of project's milestones.
#
# @example
# Gitlab.milestones(5)
#
# @param [Integer] project The ID of a project.
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def milestones(project, options={})
get("/projects/#{project}/milestones", :query => options)
end
# Gets a single milestone.
#
# @example
# Gitlab.milestone(5, 36)
#
# @param [Integer, String] project The ID of a project.
# @param [Integer] id The ID of a milestone.
# @return [Gitlab::ObjectifiedHash]
def milestone(project, id)
get("/projects/#{project}/milestones/#{id}")
end
# Creates a new milestone.
#
# @param [Integer] project The ID of a project.
# @param [String] title The title of a milestone.
# @param [Hash] options A customizable set of options.
# @option options [String] :description The description of a milestone.
# @option options [String] :due_date The due date of a milestone.
# @return [Gitlab::ObjectifiedHash] Information about created milestone.
def create_milestone(project, title, options={})
body = {:title => title}.merge(options)
post("/projects/#{project}/milestones", :body => body)
end
# Updates a milestone.
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of a milestone.
# @param [Hash] options A customizable set of options.
# @option options [String] :title The title of a milestone.
# @option options [String] :description The description of a milestone.
# @option options [String] :due_date The due date of a milestone.
# @option options [String] :state_event The state of a milestone ('close' or 'activate').
# @return [Gitlab::ObjectifiedHash] Information about updated milestone.
def edit_milestone(project, id, options={})
put("/projects/#{project}/milestones/#{id}", :body => options)
end
end
end

View File

@ -0,0 +1,106 @@
class Gitlab::Client
# Defines methods related to notes.
module Notes
# Gets a list of projects notes.
#
# @example
# Gitlab.notes(5)
#
# @param [Integer] project The ID of a project.
# @return [Array<Gitlab::ObjectifiedHash>]
def notes(project)
get("/projects/#{project}/notes")
end
# Gets a list of notes for a issue.
#
# @example
# Gitlab.issue_notes(5, 10)
#
# @param [Integer] project The ID of a project.
# @param [Integer] issue The ID of an issue.
# @return [Array<Gitlab::ObjectifiedHash>]
def issue_notes(project, issue)
get("/projects/#{project}/issues/#{issue}/notes")
end
# Gets a list of notes for a snippet.
#
# @example
# Gitlab.snippet_notes(5, 1)
#
# @param [Integer] project The ID of a project.
# @param [Integer] snippet The ID of a snippet.
# @return [Array<Gitlab::ObjectifiedHash>]
def snippet_notes(project, snippet)
get("/projects/#{project}/snippets/#{snippet}/notes")
end
# Gets a single wall note.
#
# @example
# Gitlab.note(5, 15)
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of a note.
# @return [Gitlab::ObjectifiedHash]
def note(project, id)
get("/projects/#{project}/notes/#{id}")
end
# Gets a single issue note.
#
# @example
# Gitlab.issue_note(5, 10, 1)
#
# @param [Integer] project The ID of a project.
# @param [Integer] issue The ID of an issue.
# @param [Integer] id The ID of a note.
# @return [Gitlab::ObjectifiedHash]
def issue_note(project, issue, id)
get("/projects/#{project}/issues/#{issue}/notes/#{id}")
end
# Gets a single snippet note.
#
# @example
# Gitlab.snippet_note(5, 11, 3)
#
# @param [Integer] project The ID of a project.
# @param [Integer] snippet The ID of a snippet.
# @param [Integer] id The ID of an note.
# @return [Gitlab::ObjectifiedHash]
def snippet_note(project, snippet, id)
get("/projects/#{project}/snippets/#{snippet}/notes/#{id}")
end
# Creates a new wall note.
#
# @param [Integer] project The ID of a project.
# @param [String] body The body of a note.
# @return [Gitlab::ObjectifiedHash] Information about created note.
def create_note(project, body)
post("/projects/#{project}/notes", :body => {:body => body})
end
# Creates a new issue note.
#
# @param [Integer] project The ID of a project.
# @param [Integer] issue The ID of an issue.
# @param [String] body The body of a note.
# @return [Gitlab::ObjectifiedHash] Information about created note.
def create_issue_note(project, issue, body)
post("/projects/#{project}/issues/#{issue}/notes", :body => {:body => body})
end
# Creates a new snippet note.
#
# @param [Integer] project The ID of a project.
# @param [Integer] snippet The ID of a snippet.
# @param [String] body The body of a note.
# @return [Gitlab::ObjectifiedHash] Information about created note.
def create_snippet_note(project, snippet, body)
post("/projects/#{project}/snippets/#{snippet}/notes", :body => {:body => body})
end
end
end

View File

@ -0,0 +1,300 @@
class Gitlab::Client
# Defines methods related to projects.
module Projects
# Gets a list of projects owned by the authenticated user.
#
# @example
# Gitlab.projects
#
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @option options [String] :scope Scope of projects. 'owned' for list of projects owned by the authenticated user, 'all' to get all projects (admin only)
# @return [Array<Gitlab::ObjectifiedHash>]
def projects(options={})
if (options[:scope])
get("/projects/#{options[:scope]}", :query => options)
else
get("/projects", :query => options)
end
end
# Gets information about a project.
#
# @example
# Gitlab.project(3)
# Gitlab.project('gitlab')
#
# @param [Integer, String] id The ID or name of a project.
# @return [Gitlab::ObjectifiedHash]
def project(id)
get("/projects/#{id}")
end
# Gets a list of project events.
#
# @example
# Gitlab.project_events(42)
# Gitlab.project_events('gitlab')
#
# @param [Integer, String] project The ID or name of a project.
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def project_events(project, options={})
get("/projects/#{project}/events", :query => options)
end
# Creates a new project.
#
# @example
# Gitlab.create_project('gitlab')
# Gitlab.create_project('viking', :description => 'Awesome project')
# Gitlab.create_project('Red', :wall_enabled => false)
#
# @param [String] name The name of a project.
# @param [Hash] options A customizable set of options.
# @option options [String] :description The description of a project.
# @option options [String] :default_branch The default branch of a project.
# @option options [String] :group_id The group in which to create a project.
# @option options [String] :namespace_id The namespace in which to create a project.
# @option options [Boolean] :wiki_enabled The wiki integration for a project (0 = false, 1 = true).
# @option options [Boolean] :wall_enabled The wall functionality for a project (0 = false, 1 = true).
# @option options [Boolean] :issues_enabled The issues integration for a project (0 = false, 1 = true).
# @option options [Boolean] :snippets_enabled The snippets integration for a project (0 = false, 1 = true).
# @option options [Boolean] :merge_requests_enabled The merge requests functionality for a project (0 = false, 1 = true).
# @option options [Boolean] :public The setting for making a project public (0 = false, 1 = true).
# @option options [Integer] :user_id The user/owner id of a project.
# @return [Gitlab::ObjectifiedHash] Information about created project.
def create_project(name, options={})
url = options[:user_id] ? "/projects/user/#{options[:user_id]}" : "/projects"
post(url, :body => {:name => name}.merge(options))
end
# Deletes a project.
#
# @example
# Gitlab.delete_project(4)
#
# @param [Integer, String] id The ID or name of a project.
# @return [Gitlab::ObjectifiedHash] Information about deleted project.
def delete_project(id)
delete("/projects/#{id}")
end
# Gets a list of project team members.
#
# @example
# Gitlab.team_members(42)
# Gitlab.team_members('gitlab')
#
# @param [Integer, String] project The ID or name of a project.
# @param [Hash] options A customizable set of options.
# @option options [String] :query The search query.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def team_members(project, options={})
get("/projects/#{project}/members", :query => options)
end
# Gets a project team member.
#
# @example
# Gitlab.team_member('gitlab', 2)
#
# @param [Integer, String] project The ID or name of a project.
# @param [Integer] id The ID of a project team member.
# @return [Gitlab::ObjectifiedHash]
def team_member(project, id)
get("/projects/#{project}/members/#{id}")
end
# Adds a user to project team.
#
# @example
# Gitlab.add_team_member('gitlab', 2, 40)
#
# @param [Integer, String] project The ID or name of a project.
# @param [Integer] id The ID of a user.
# @param [Integer] access_level The access level to project.
# @param [Hash] options A customizable set of options.
# @return [Gitlab::ObjectifiedHash] Information about added team member.
def add_team_member(project, id, access_level)
post("/projects/#{project}/members", :body => {:user_id => id, :access_level => access_level})
end
# Updates a team member's project access level.
#
# @example
# Gitlab.edit_team_member('gitlab', 3, 20)
#
# @param [Integer, String] project The ID or name of a project.
# @param [Integer] id The ID of a user.
# @param [Integer] access_level The access level to project.
# @param [Hash] options A customizable set of options.
# @return [Array<Gitlab::ObjectifiedHash>] Information about updated team member.
def edit_team_member(project, id, access_level)
put("/projects/#{project}/members/#{id}", :body => {:access_level => access_level})
end
# Removes a user from project team.
#
# @example
# Gitlab.remove_team_member('gitlab', 2)
#
# @param [Integer, String] project The ID or name of a project.
# @param [Integer] id The ID of a user.
# @param [Hash] options A customizable set of options.
# @return [Gitlab::ObjectifiedHash] Information about removed team member.
def remove_team_member(project, id)
delete("/projects/#{project}/members/#{id}")
end
# Gets a list of project hooks.
#
# @example
# Gitlab.project_hooks(42)
# Gitlab.project_hooks('gitlab')
#
# @param [Integer, String] project The ID or name of a project.
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def project_hooks(project, options={})
get("/projects/#{project}/hooks", :query => options)
end
# Gets a project hook.
#
# @example
# Gitlab.project_hook(42, 5)
# Gitlab.project_hook('gitlab', 5)
#
# @param [Integer, String] project The ID or name of a project.
# @param [Integer] id The ID of a hook.
# @return [Gitlab::ObjectifiedHash]
def project_hook(project, id)
get("/projects/#{project}/hooks/#{id}")
end
# Adds a new hook to the project.
#
# @example
# Gitlab.add_project_hook(42, 'https://api.example.net/v1/webhooks/ci')
#
# @param [Integer, String] project The ID or name of a project.
# @param [String] url The hook URL.
# @param [Hash] options Events list (`{push_events: true, merge_requests_events: false}`).
# @return [Gitlab::ObjectifiedHash] Information about added hook.
def add_project_hook(project, url, options = {})
available_events = [:push_events, :merge_requests_events, :issues_events]
passed_events = available_events.select { |event| options[event] }
events = Hash[passed_events.map { |event| [event, options[event]] }]
post("/projects/#{project}/hooks", :body => {:url => url}.merge(events))
end
# Updates a project hook URL.
#
# @example
# Gitlab.edit_project_hook(42, 1, 'https://api.example.net/v1/webhooks/ci')
#
# @param [Integer, String] project The ID or name of a project.
# @param [Integer] id The ID of the hook.
# @param [String] url The hook URL.
# @return [Gitlab::ObjectifiedHash] Information about updated hook.
def edit_project_hook(project, id, url)
put("/projects/#{project}/hooks/#{id}", :body => {:url => url})
end
# Deletes a hook from project.
#
# @example
# Gitlab.delete_project_hook('gitlab', 4)
#
# @param [Integer, String] project The ID or name of a project.
# @param [String] id The ID of the hook.
# @return [Gitlab::ObjectifiedHash] Information about deleted hook.
def delete_project_hook(project, id)
delete("/projects/#{project}/hooks/#{id}")
end
# Mark this project as forked from the other
#
# @example
# Gitlab.make_forked(42, 24)
#
# @param [Integer, String] project The ID or name of a project.
# @param [Integer] id The ID of the project it is forked from.
# @return [Gitlab::ObjectifiedHash] Information about the forked project.
def make_forked_from(project, id)
post("/projects/#{project}/fork/#{id}")
end
# Remove a forked_from relationship for a project.
#
# @example
# Gitlab.remove_forked(42)
#
# @param [Integer, String] project The ID or name of a project.
# @param [Integer] project The ID of the project it is forked from
# @return [Gitlab::ObjectifiedHash] Information about the forked project.
def remove_forked(project)
delete("/projects/#{project}/fork")
end
# Gets a project deploy keys.
#
# @example
# Gitlab.deploy_keys(42)
#
# @param [Integer] project The ID of a project.
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def deploy_keys(project, options={})
get("/projects/#{project}/keys", :query => options)
end
# Gets a single project deploy key.
#
# @example
# Gitlab.deploy_key(42, 1)
#
# @param [Integer, String] project The ID of a project.
# @param [Integer] id The ID of a deploy key.
# @return [Gitlab::ObjectifiedHash]
def deploy_key(project, id)
get("/projects/#{project}/keys/#{id}")
end
# Creates a new deploy key.
#
# @example
# Gitlab.create_deploy_key(42, 'My Key', 'Key contents')
#
# @param [Integer] project The ID of a project.
# @param [String] title The title of a deploy key.
# @param [String] key The content of a deploy key.
# @return [Gitlab::ObjectifiedHash] Information about created deploy key.
def create_deploy_key(project, title, key)
post("/projects/#{project}/keys", body: {title: title, key: key})
end
# Deletes a deploy key from project.
#
# @example
# Gitlab.delete_deploy_key(42, 1)
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of a deploy key.
# @return [Gitlab::ObjectifiedHash] Information about deleted deploy key.
def delete_deploy_key(project, id)
delete("/projects/#{project}/keys/#{id}")
end
end
end

View File

@ -0,0 +1,89 @@
class Gitlab::Client
# Defines methods related to repositories.
module Repositories
def trees(project, options={})
get "/projects/#{project}/repository/tree", query: options
end
alias_method :repo_trees, :trees
def files(project, file_path, ref)
get "/projects/#{project}/repository/files", query: {file_path: file_path, ref: ref}
end
alias_method :repo_files, :files
# Gets a list of project repository tags.
#
# @example
# Gitlab.tags(42)
#
# @param [Integer] project The ID of a project.
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def tags(project, options={})
get("/projects/#{project}/repository/tags", :query => options)
end
alias_method :repo_tags, :tags
# Creates a new project repository tag.
#
# @example
# Gitlab.create_tag(42,'new_tag','master')
#
# @param [Integer] project The ID of a project.
# @param [String] tag_name The name of the new tag.
# @param [String] ref The ref (commit sha, branch name, or another tag) the tag will point to.
# @return [Gitlab::ObjectifiedHash]
def create_tag(project, tag_name, ref)
post("/projects/#{project}/repository/tags", body: {tag_name: tag_name, ref: ref})
end
alias_method :repo_create_tag, :create_tag
# Gets a list of project commits.
#
# @example
# Gitlab.commits('viking')
# Gitlab.repo_commits('gitlab', :ref_name => 'api')
#
# @param [Integer] project The ID of a project.
# @param [Hash] options A customizable set of options.
# @option options [String] :ref_name The branch or tag name of a project repository.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def commits(project, options={})
get("/projects/#{project}/repository/commits", :query => options)
end
alias_method :repo_commits, :commits
# Gets a specific commit identified by the commit hash or name of a branch or tag.
#
# @example
# Gitlab.commit(42, '6104942438c14ec7bd21c6cd5bd995272b3faff6')
# Gitlab.repo_commit(3, 'ed899a2f4b50b4370feeea94676502b42383c746')
#
# @param [Integer] project The ID of a project.
# @param [String] sha The commit hash or name of a repository branch or tag
# @return [Gitlab::ObjectifiedHash]
def commit(project, sha)
get("/projects/#{project}/repository/commits/#{sha}")
end
alias_method :repo_commit, :commit
# Get the diff of a commit in a project.
#
# @example
# Gitlab.commit_diff(42, '6104942438c14ec7bd21c6cd5bd995272b3faff6')
# Gitlab.repo_commit_diff(3, 'ed899a2f4b50b4370feeea94676502b42383c746')
#
# @param [Integer] project The ID of a project.
# @param [String] sha The name of a repository branch or tag or if not given the default branch.
# @return [Gitlab::ObjectifiedHash]
def commit_diff(project, sha)
get("/projects/#{project}/repository/commits/#{sha}/diff")
end
alias_method :repo_commit_diff, :commit_diff
end
end

View File

@ -0,0 +1,86 @@
class Gitlab::Client
# Defines methods related to snippets.
module Snippets
# Gets a list of project's snippets.
#
# @example
# Gitlab.snippets(42)
#
# @param [Integer] project The ID of a project.
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Gitlab::ObjectifiedHash]
def snippets(project, options={})
get("/projects/#{project}/snippets", :query => options)
end
# Gets information about a snippet.
#
# @example
# Gitlab.snippet(2, 14)
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of a snippet.
# @return [Gitlab::ObjectifiedHash]
def snippet(project, id)
get("/projects/#{project}/snippets/#{id}")
end
# Creates a new snippet.
#
# @example
# Gitlab.create_snippet(42, {:title => 'REST', :file_name => 'api.rb', :code => 'some code'})
#
# @param [Integer] project The ID of a project.
# @param [Hash] options A customizable set of options.
# @option options [String] :title (required) The title of a snippet.
# @option options [String] :file_name (required) The name of a snippet file.
# @option options [String] :code (required) The content of a snippet.
# @option options [String] :lifetime (optional) The expiration date of a snippet.
# @return [Gitlab::ObjectifiedHash] Information about created snippet.
def create_snippet(project, options={})
check_attributes!(options, [:title, :file_name, :code])
post("/projects/#{project}/snippets", :body => options)
end
# Updates a snippet.
#
# @example
# Gitlab.edit_snippet(42, 34, :file_name => 'README.txt')
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of a snippet.
# @param [Hash] options A customizable set of options.
# @option options [String] :title The title of a snippet.
# @option options [String] :file_name The name of a snippet file.
# @option options [String] :code The content of a snippet.
# @option options [String] :lifetime The expiration date of a snippet.
# @return [Gitlab::ObjectifiedHash] Information about updated snippet.
def edit_snippet(project, id, options={})
put("/projects/#{project}/snippets/#{id}", :body => options)
end
# Deletes a snippet.
#
# @example
# Gitlab.delete_snippet(2, 14)
#
# @param [Integer] project The ID of a project.
# @param [Integer] id The ID of a snippet.
# @return [Gitlab::ObjectifiedHash] Information about deleted snippet.
def delete_snippet(project, id)
delete("/projects/#{project}/snippets/#{id}")
end
private
def check_attributes!(options, attrs)
attrs.each do |attr|
unless options.has_key?(attr) || options.has_key?(attr.to_s)
raise Gitlab::Error::MissingAttributes.new("Missing '#{attr}' parameter")
end
end
end
end
end

View File

@ -0,0 +1,58 @@
class Gitlab::Client
# Defines methods related to system hooks.
module SystemHooks
# Gets a list of system hooks.
#
# @example
# Gitlab.hooks
# Gitlab.system_hooks
#
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def hooks(options={})
get("/hooks", query: options)
end
alias_method :system_hooks, :hooks
# Adds a new system hook.
#
# @example
# Gitlab.add_hook('http://example.com/hook')
# Gitlab.add_system_hook('https://api.example.net/v1/hook')
#
# @param [String] url The hook URL.
# @return [Gitlab::ObjectifiedHash]
def add_hook(url)
post("/hooks", :body => {:url => url})
end
alias_method :add_system_hook, :add_hook
# Tests a system hook.
#
# @example
# Gitlab.hook(3)
# Gitlab.system_hook(12)
#
# @param [Integer] id The ID of a system hook.
# @return [Array<Gitlab::ObjectifiedHash>]
def hook(id)
get("/hooks/#{id}")
end
alias_method :system_hook, :hook
# Deletes a new system hook.
#
# @example
# Gitlab.delete_hook(3)
# Gitlab.delete_system_hook(12)
#
# @param [Integer] id The ID of a system hook.
# @return [Gitlab::ObjectifiedHash]
def delete_hook(id)
delete("/hooks/#{id}")
end
alias_method :delete_system_hook, :delete_hook
end
end

View File

@ -0,0 +1,123 @@
class Gitlab::Client
# Defines methods related to users.
module Users
# Gets a list of users.
#
# @example
# Gitlab.users
#
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def users(options={})
get("/users", :query => options)
end
# Gets information about a user.
# Will return information about an authorized user if no ID passed.
#
# @example
# Gitlab.user
# Gitlab.user(2)
#
# @param [Integer] id The ID of a user.
# @return [Gitlab::ObjectifiedHash]
def user(id=nil)
id.to_i.zero? ? get("/user") : get("/users/#{id}")
end
# Creates a new user.
# Requires authentication from an admin account.
#
# @param [String] email The email of a user.
# @param [String] password The password of a user.
# @param [Hash] options A customizable set of options.
# @option options [String] :name The name of a user. Defaults to email.
# @option options [String] :skype The skype of a user.
# @option options [String] :linkedin The linkedin of a user.
# @option options [String] :twitter The twitter of a user.
# @option options [Integer] :projects_limit The limit of projects for a user.
# @return [Gitlab::ObjectifiedHash] Information about created user.
def create_user(email, password, options={})
body = {:email => email, :password => password, :name => email}.merge(options)
post("/users", :body => body)
end
# Updates a user.
#
# @param [Integer] id The ID of a user.
# @param [Hash] options A customizable set of options.
# @option options [String] email The email of a user.
# @option options [String] password The password of a user.
# @option options [String] :name The name of a user. Defaults to email.
# @option options [String] :skype The skype of a user.
# @option options [String] :linkedin The linkedin of a user.
# @option options [String] :twitter The twitter of a user.
# @option options [Integer] :projects_limit The limit of projects for a user.
# @return [Gitlab::ObjectifiedHash] Information about created user.
def edit_user(user_id, options={})
put("/users/#{user_id}", :body => options)
end
# Creates a new user session.
#
# @example
# Gitlab.session('jack@example.com', 'secret12345')
#
# @param [String] email The email of a user.
# @param [String] password The password of a user.
# @return [Gitlab::ObjectifiedHash]
# @note This method doesn't require private_token to be set.
def session(email, password)
post("/session", :body => {:email => email, :password => password})
end
# Gets a list of user's SSH keys.
#
# @example
# Gitlab.ssh_keys
#
# @param [Hash] options A customizable set of options.
# @option options [Integer] :page The page number.
# @option options [Integer] :per_page The number of results per page.
# @return [Array<Gitlab::ObjectifiedHash>]
def ssh_keys(options={})
get("/user/keys", :query => options)
end
# Gets information about SSH key.
#
# @example
# Gitlab.ssh_key(1)
#
# @param [Integer] id The ID of a user's SSH key.
# @return [Gitlab::ObjectifiedHash]
def ssh_key(id)
get("/user/keys/#{id}")
end
# Creates a new SSH key.
#
# @example
# Gitlab.create_ssh_key('key title', 'key body')
#
# @param [String] title The title of an SSH key.
# @param [String] key The SSH key body.
# @return [Gitlab::ObjectifiedHash] Information about created SSH key.
def create_ssh_key(title, key)
post("/user/keys", :body => {:title => title, :key => key})
end
# Deletes an SSH key.
#
# @example
# Gitlab.delete_ssh_key(1)
#
# @param [Integer] id The ID of a user's SSH key.
# @return [Gitlab::ObjectifiedHash] Information about deleted SSH key.
def delete_ssh_key(id)
delete("/user/keys/#{id}")
end
end
end

View File

@ -0,0 +1,39 @@
module Gitlab
# Defines constants and methods related to configuration.
module Configuration
# An array of valid keys in the options hash when configuring a Gitlab::API.
VALID_OPTIONS_KEYS = [:endpoint, :private_token, :user_agent, :sudo, :httparty].freeze
# The user agent that will be sent to the API endpoint if none is set.
DEFAULT_USER_AGENT = "Gitlab Ruby Gem #{Gitlab::VERSION}".freeze
# @private
attr_accessor(*VALID_OPTIONS_KEYS)
# Sets all configuration options to their default values
# when this module is extended.
def self.extended(base)
base.reset
end
# Convenience method to allow configuration options to be set in a block.
def configure
yield self
end
# Creates a hash of options and their values.
def options
VALID_OPTIONS_KEYS.inject({}) do |option, key|
option.merge!(key => send(key))
end
end
# Resets all configuration options to the defaults.
def reset
self.endpoint = ENV['GITLAB_API_ENDPOINT']
self.private_token = ENV['GITLAB_API_PRIVATE_TOKEN']
self.sudo = nil
self.user_agent = DEFAULT_USER_AGENT
end
end
end

View File

@ -0,0 +1,42 @@
module Gitlab
module Error
# Custom error class for rescuing from all Gitlab errors.
class Error < StandardError; end
# Raise when attributes are missing.
class MissingAttributes < Error; end
# Raised when API endpoint credentials not configured.
class MissingCredentials < Error; end
# Raised when impossible to parse response body.
class Parsing < Error; end
# Raised when API endpoint returns the HTTP status code 400.
class BadRequest < Error; end
# Raised when API endpoint returns the HTTP status code 401.
class Unauthorized < Error; end
# Raised when API endpoint returns the HTTP status code 403.
class Forbidden < Error; end
# Raised when API endpoint returns the HTTP status code 404.
class NotFound < Error; end
# Raised when API endpoint returns the HTTP status code 405.
class MethodNotAllowed < Error; end
# Raised when API endpoint returns the HTTP status code 409.
class Conflict < Error; end
# Raised when API endpoint returns the HTTP status code 500.
class InternalServerError < Error; end
# Raised when API endpoint returns the HTTP status code 502.
class BadGateway < Error; end
# Raised when API endpoint returns the HTTP status code 503.
class ServiceUnavailable < Error; end
end
end

View File

@ -0,0 +1,44 @@
require 'gitlab'
require 'gitlab/cli_helpers'
module Gitlab::Help
extend Gitlab::CLI::Helpers
def self.get_help(methods,cmd=nil)
help = ''
if cmd.nil? || cmd == 'help'
help = actions_table
else
ri_cmd = `which ri`.chomp
if $? == 0
namespace = methods.select {|m| m[:name] === cmd }.map {|m| m[:owner]+'.'+m[:name] }.shift
if namespace
begin
ri_output = `#{ri_cmd} -T #{namespace} 2>&1`.chomp
if $? == 0
ri_output.gsub!(/#{cmd}\((.*?)\)/, cmd+' \1')
ri_output.gsub!(/Gitlab\./, 'gitlab> ')
ri_output.gsub!(/Gitlab\..+$/, '')
ri_output.gsub!(/\,\s?/, ' ')
help = ri_output
else
help = "Ri docs not found for #{namespace}, please install the docs to use 'help'"
end
rescue => e
puts e.message
end
else
help = "Unknown command: #{cmd}"
end
else
help = "'ri' tool not found in your PATH, please install it to use the help."
end
end
puts help
end
end

View File

@ -0,0 +1,24 @@
module Gitlab
# Converts hashes to the objects.
class ObjectifiedHash
# Creates a new ObjectifiedHash object.
def initialize(hash)
@hash = hash
@data = hash.inject({}) do |data, (key,value)|
value = ObjectifiedHash.new(value) if value.is_a? Hash
data[key.to_s] = value
data
end
end
def to_hash
@hash
end
alias_method :to_h, :to_hash
# Delegate to ObjectifiedHash.
def method_missing(key)
@data.key?(key.to_s) ? @data[key.to_s] : nil
end
end
end

View File

@ -0,0 +1,113 @@
require 'httparty'
require 'json'
module Gitlab
# @private
class Request
include HTTParty
format :json
headers 'Accept' => 'application/json'
parser Proc.new { |body, _| parse(body) }
attr_accessor :private_token
# Converts the response body to an ObjectifiedHash.
def self.parse(body)
body = decode(body)
if body.is_a? Hash
ObjectifiedHash.new body
elsif body.is_a? Array
body.collect! { |e| ObjectifiedHash.new(e) }
else
raise Error::Parsing.new "Couldn't parse a response body"
end
end
# Decodes a JSON response into Ruby object.
def self.decode(response)
begin
JSON.load response
rescue JSON::ParserError
raise Error::Parsing.new "The response is not a valid JSON"
end
end
def get(path, options={})
set_httparty_config(options)
set_private_token_header(options)
validate self.class.get(path, options)
end
def post(path, options={})
set_httparty_config(options)
set_private_token_header(options, path)
validate self.class.post(path, options)
end
def put(path, options={})
set_httparty_config(options)
set_private_token_header(options)
validate self.class.put(path, options)
end
def delete(path, options={})
set_httparty_config(options)
set_private_token_header(options)
validate self.class.delete(path, options)
end
# Checks the response code for common errors.
# Returns parsed response for successful requests.
def validate(response)
case response.code
when 400; raise Error::BadRequest.new error_message(response)
when 401; raise Error::Unauthorized.new error_message(response)
when 403; raise Error::Forbidden.new error_message(response)
when 404; raise Error::NotFound.new error_message(response)
when 405; raise Error::MethodNotAllowed.new error_message(response)
when 409; raise Error::Conflict.new error_message(response)
when 500; raise Error::InternalServerError.new error_message(response)
when 502; raise Error::BadGateway.new error_message(response)
when 503; raise Error::ServiceUnavailable.new error_message(response)
end
response.parsed_response
end
# Sets a base_uri and default_params for requests.
# @raise [Error::MissingCredentials] if endpoint not set.
def set_request_defaults(endpoint, private_token, sudo=nil)
raise Error::MissingCredentials.new("Please set an endpoint to API") unless endpoint
@private_token = private_token
self.class.base_uri endpoint
self.class.default_params :sudo => sudo
self.class.default_params.delete(:sudo) if sudo.nil?
end
private
# Sets a PRIVATE-TOKEN header for requests.
# @raise [Error::MissingCredentials] if private_token not set.
def set_private_token_header(options, path=nil)
unless path == '/session'
raise Error::MissingCredentials.new("Please set a private_token for user") unless @private_token
options[:headers] = {'PRIVATE-TOKEN' => @private_token}
end
end
# Set HTTParty configuration
# @see https://github.com/jnunemaker/httparty
def set_httparty_config(options)
if self.httparty
options.merge!(self.httparty)
end
end
def error_message(response)
"Server responded with code #{response.code}, message: #{response.parsed_response.message}. " \
"Request URI: #{response.request.base_uri}#{response.request.path}"
end
end
end

View File

@ -0,0 +1,51 @@
require 'gitlab'
require 'gitlab/help'
require 'gitlab/cli_helpers'
require 'readline'
class Gitlab::Shell
extend Gitlab::CLI::Helpers
def self.start
actions = Gitlab.actions
comp = proc { |s| actions.map(&:to_s).grep(/^#{Regexp.escape(s)}/) }
Readline.completion_proc = comp
Readline.completion_append_character = ' '
client = Gitlab::Client.new(endpoint: '')
while buf = Readline.readline('gitlab> ', true)
next if buf.nil? || buf.empty?
break if buf == 'exit'
buf = buf.scan(/["][^"]+["]|\S+/).map { |word| word.gsub(/^['"]|['"]$/,'') }
cmd = buf.shift
args = buf.count > 0 ? buf : []
if cmd == 'help'
methods = []
actions.each do |action|
methods << {
name: action.to_s,
owner: client.method(action).owner.to_s
}
end
args[0].nil? ? Gitlab::Help.get_help(methods) : Gitlab::Help.get_help(methods, args[0])
next
end
data = if actions.include?(cmd.to_sym)
confirm_command(cmd)
gitlab_helper(cmd, args)
else
"'#{cmd}' is not a valid command. See the 'help' for a list of valid commands."
end
output_table(cmd, args, data)
end
end
end

View File

@ -0,0 +1,3 @@
module Gitlab
VERSION = "3.2.0"
end

View File

@ -0,0 +1 @@
{"name":"api","commit":{"id":"f7dd067490fe57505f7226c3b54d3127d2f7fd46","parents":[{"id":"949b1df930bedace1dbd755aaa4a82e8c451a616"}],"tree":"f8c4b21c036339f92fcc5482aa28a41250553b27","message":"API: expose issues project id","author":{"name":"Nihad Abbasov","email":"narkoz.2008@gmail.com"},"committer":{"name":"Nihad Abbasov","email":"narkoz.2008@gmail.com"},"authored_date":"2012-07-25T04:22:21-07:00","committed_date":"2012-07-25T04:22:21-07:00"},"protected": true}

View File

@ -0,0 +1 @@
[{"name":"api","commit":{"id":"f7dd067490fe57505f7226c3b54d3127d2f7fd46","parents":[{"id":"949b1df930bedace1dbd755aaa4a82e8c451a616"}],"tree":"f8c4b21c036339f92fcc5482aa28a41250553b27","message":"API: expose issues project id","author":{"name":"Nihad Abbasov","email":"narkoz.2008@gmail.com"},"committer":{"name":"Nihad Abbasov","email":"narkoz.2008@gmail.com"},"authored_date":"2012-07-25T04:22:21-07:00","committed_date":"2012-07-25T04:22:21-07:00"}},{"name":"dashboard-feed","commit":{"id":"f8f6ff065eccc6ede4d35ed87a09bb962b84ca25","parents":[{"id":"2cf8010792c3075824ee27d0f037aeb178cbbf7e"}],"tree":"e17f2157143d550891d4669c10b7446e4739bc6d","message":"add projects atom feed","author":{"name":"Nihad Abbasov","email":"narkoz.2008@gmail.com"},"committer":{"name":"Nihad Abbasov","email":"narkoz.2008@gmail.com"},"authored_date":"2012-05-31T23:42:02-07:00","committed_date":"2012-05-31T23:42:02-07:00"}},{"name":"master","commit":{"id":"2cf8010792c3075824ee27d0f037aeb178cbbf7e","parents":[{"id":"af226ae9c9af406c8a0e0bbdf364563495c2f432"},{"id":"e851cb07762aa464aae10e8b4b28de87c1a6f925"}],"tree":"6c6845838039f01723d91f395a1d2fa1dcc82522","message":"Merge pull request #868 from SaitoWu/bugfix/encoding\n\nBugfix/encoding","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-05-30T00:24:43-07:00","committed_date":"2012-05-30T00:24:43-07:00"}},{"name":"preview_notes","commit":{"id":"3749e0d99ac6bfbc65889b1b7a5310e14e7fe89a","parents":[{"id":"2483181f2c3d4ea7d2c68147b19bc07fc3937b0c"}],"tree":"f8c56161b0d6561568f088df9961362eb1ece88b","message":"pass project_id to notes preview path","author":{"name":"Nihad Abbasov","email":"narkoz.2008@gmail.com"},"committer":{"name":"Nihad Abbasov","email":"narkoz.2008@gmail.com"},"authored_date":"2012-08-09T23:46:27-07:00","committed_date":"2012-08-09T23:46:27-07:00"}},{"name":"refactoring","commit":{"id":"7c7761099cae83f59fe5780340e100be890847b2","parents":[{"id":"058d80b3363dd4fc4417ca4f60f76119188a2470"}],"tree":"d7d4a94c700dc0e84ee943019213d2358a49c413","message":"fix deprecation warnings","author":{"name":"Nihad Abbasov","email":"narkoz.2008@gmail.com"},"committer":{"name":"Nihad Abbasov","email":"narkoz.2008@gmail.com"},"authored_date":"2012-05-29T07:16:28-07:00","committed_date":"2012-05-29T07:16:28-07:00"}}]

View File

@ -0,0 +1 @@
{"note":"Cool Merge Request!","author":{"id":1,"username":"jsmith","email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-07-11T01:32:18Z"}}

View File

@ -0,0 +1 @@
{"name":"api","commit":{ "id":"f7dd067490fe57505f7226c3b54d3127d2f7fd46","message":"API: expose issues project id","parent_ids":["949b1df930bedace1dbd755aaa4a82e8c451a616"],"authored_date":"2012-07-25T04:22:21-07:00","author_name":"Nihad Abbasov","author_email":"narkoz.2008@gmail.com","committed_date":"2012-07-25T04:22:21-07:00","committer_name":"Nihad Abbasov","committer_email":"narkoz.2008@gmail.com"},"protected": false}

View File

@ -0,0 +1 @@
{"id":2,"target_branch":"master","source_branch":"api","project_id":3,"title":"New feature","closed":false,"merged":false,"author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-10-19T05:56:05Z"},"assignee":{"id":2,"email":"jack@example.com","name":"Jack Smith","blocked":false,"created_at":"2012-10-19T05:56:14Z"}}

View File

@ -0,0 +1 @@
{"message": "409 Already exists"}

60
lib/gitlab-cli/spec/fixtures/group.json vendored Normal file
View File

@ -0,0 +1,60 @@
{"id": 10, "name": "GitLab-Group", "path": "gitlab-group", "owner_id": 6, "projects": [
{
"id": 9,
"name": "mojito",
"description": null,
"default_branch": "master",
"owner": {
"id": 6,
"username": "jose",
"email": "jose@abc.com",
"name": "Jose Jose",
"blocked": false,
"created_at": "2013-02-06T06:54:06Z"
},
"path": "mojito",
"path_with_namespace": "gitlab-group/mojito",
"issues_enabled": true,
"merge_requests_enabled": true,
"wall_enabled": true,
"wiki_enabled": true,
"created_at": "2013-02-06T16:59:15Z",
"namespace": {
"created_at": "2013-02-06T16:58:22Z",
"id": 10,
"name": "GitLab-Group",
"owner_id": 6,
"path": "gitlab-group",
"updated_at": "2013-02-06T16:58:22Z"
}
},
{
"id": 10,
"name": "gitlabhq",
"description": null,
"default_branch": null,
"owner": {
"id": 6,
"username": "randx",
"email": "randx@github.com",
"name": "Dmitry Z",
"blocked": false,
"created_at": "2013-02-06T06:54:06Z"
},
"path": "gitlabhq",
"path_with_namespace": "gitlab-group/gitlabhq",
"issues_enabled": true,
"merge_requests_enabled": true,
"wall_enabled": true,
"wiki_enabled": true,
"created_at": "2013-02-06T17:02:31Z",
"namespace": {
"created_at": "2013-02-06T16:58:22Z",
"id": 10,
"name": "GitLab-Group",
"owner_id": 6,
"path": "gitlab-group",
"updated_at": "2013-02-06T16:58:22Z"
}
}
]}

View File

@ -0,0 +1 @@
{"id":3,"name":"Gitlab-Group","path":"gitlab-group","owner_id":1}

View File

@ -0,0 +1 @@
{"id":2,"username":"jsmith","email":"jsmith@local.host","name":"John Smith","state":"active","created_at":"2013-09-04T18:15:30Z","access_level":10}

View File

@ -0,0 +1 @@
{"created_at":"2013-09-04T18:18:15Z","group_access":10,"group_id":3,"id":2,"notification_level":3,"updated_at":"2013-09-04T18:18:15Z","user_id":2}

View File

@ -0,0 +1 @@
[{"id":1,"username":"eraymond","email":"eraymond@local.host","name":"Edward Raymond","state":"active","created_at":"2013-08-30T16:16:22Z","access_level":50},{"id":1,"username":"jsmith","email":"jsmith@local.host","name":"John Smith","state":"active","created_at":"2013-08-30T16:16:22Z","access_level":50}]

View File

@ -0,0 +1,2 @@
[{"id": 3,"name": "ThreeGroup","path": "threegroup","owner_id": 1},{"id": 5,"name": "Five-Group","path": "five-group","owner_id": 2},{"id": 8,"name": "Eight Group","path": "eight-group","owner_id": 6}
]

View File

@ -0,0 +1 @@
{"id":33,"project_id":3,"title":"Beatae possimus nostrum nihil reiciendis laboriosam nihil delectus alias accusantium dolor unde.","description":null,"labels":[],"milestone":null,"assignee":{"id":2,"email":"jack@example.com","name":"Jack Smith","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"author":{"id":2,"email":"jack@example.com","name":"Jack Smith","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"}

View File

@ -0,0 +1 @@
[{"id":1,"project_id":1,"title":"Culpa eius recusandae suscipit autem distinctio dolorum.","description":null,"labels":[],"milestone":null,"assignee":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":6,"project_id":2,"title":"Ut in dolorum omnis sed sit aliquam.","description":null,"labels":[],"milestone":null,"assignee":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":12,"project_id":3,"title":"Veniam et tempore quidem eum reprehenderit cupiditate non aut velit eaque.","description":null,"labels":[],"milestone":null,"assignee":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":21,"project_id":1,"title":"Vitae ea aliquam et quo eligendi sapiente voluptatum labore hic nihil culpa.","description":null,"labels":[],"milestone":null,"assignee":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":26,"project_id":2,"title":"Quo enim est nihil atque placeat voluptas neque eos voluptas.","description":null,"labels":[],"milestone":null,"assignee":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":32,"project_id":3,"title":"Deserunt tenetur impedit est beatae voluptas voluptas quaerat quisquam.","description":null,"labels":[],"milestone":null,"assignee":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"}]

1
lib/gitlab-cli/spec/fixtures/key.json vendored Normal file
View File

@ -0,0 +1 @@
{"id":1,"title":"narkoz@helium","key":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkUsh42Nh1yefGd1jbSELn5XsY8p5Oxmau0/1HqHnjuYOaj5X+kHccFDwtmtg9Ox8ua/+WptNsiE8IUwsD3zKgEjajgwq3gMeeFdxfXwM+tEvHOOMV9meRrgRWGYCToPbT6sR7/YMAYa7cPqWSpx/oZhYfz4XtoMv3ZZT1fZMmx3MY3HwXwW8j+obJyN2K4LN0TFi9RPgWWYn0DCyb9OccmABimt3i74WoJ/OT8r6/7swce8+OSe0Q2wBhyTtvxg2vtUcoek8Af+EZaUMBwSEzEsocOCzwQvjF5XUk5o7dJ8nP8W3RE60JWX57t16eQm7lBmumLYfszpn2isd6W7a1 narkoz@helium"}

View File

@ -0,0 +1 @@
[{"id":1,"title":"narkoz@helium","key":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkUsh42Nh1yefGd1jbSELn5XsY8p5Oxmau0/1HqHnjuYOaj5X+kHccFDwtmtg9Ox8ua/+WptNsiE8IUwsD3zKgEjajgwq3gMeeFdxfXwM+tEvHOOMV9meRrgRWGYCToPbT6sR7/YMAYa7cPqWSpx/oZhYfz4XtoMv3ZZT1fZMmx3MY3HwXwW8j+obJyN2K4LN0TFi9RPgWWYn0DCyb9OccmABimt3i74WoJ/OT8r6/7swce8+OSe0Q2wBhyTtvxg2vtUcoek8Af+EZaUMBwSEzEsocOCzwQvjF5XUk5o7dJ8nP8W3RE60JWX57t16eQm7lBmumLYfszpn2isd6W7a1 narkoz@helium"}]

View File

@ -0,0 +1 @@
{"id":1,"target_branch":"master","source_branch":"api","project_id":3,"title":"New feature","closed":false,"merged":false,"author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-10-19T05:56:05Z"},"assignee":{"id":2,"email":"jack@example.com","name":"Jack Smith","blocked":false,"created_at":"2012-10-19T05:56:14Z"}}

View File

@ -0,0 +1 @@
[{"note":"this is the 1st comment on the 2merge merge request","author":{"id":11,"username":"admin","email":"admin@example.com","name":"A User","state":"active","created_at":"2014-03-06T08:17:35.000Z"}},{"note":"another discussion point on the 2merge request","author":{"id":12,"username":"admin","email":"admin@example.com","name":"A User","state":"active","created_at":"2014-03-06T08:17:35.000Z"}}]

View File

@ -0,0 +1 @@
[{"id":1,"target_branch":"master","source_branch":"api","project_id":3,"title":"New feature","closed":false,"merged":false,"author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-10-19T05:56:05Z"},"assignee":{"id":2,"email":"jack@example.com","name":"Jack Smith","blocked":false,"created_at":"2012-10-19T05:56:14Z"}}]

View File

@ -0,0 +1 @@
{"id":1,"project_id":3,"title":"3.0","description":"","due_date":"2012-10-22","closed":false,"updated_at":"2012-09-17T10:15:31Z","created_at":"2012-09-17T10:15:31Z"}

View File

@ -0,0 +1 @@
[{"id":1,"project_id":3,"title":"3.0","description":"","due_date":"2012-10-22","closed":false,"updated_at":"2012-09-17T10:15:31Z","created_at":"2012-09-17T10:15:31Z"}]

View File

@ -0,0 +1 @@
{"id":1201,"body":"The solution is rather tricky","author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"created_at":"2012-11-27T19:16:44Z"}

View File

@ -0,0 +1 @@
[{"id":1201,"body":"The solution is rather tricky","author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"created_at":"2012-11-27T19:16:44Z"},{"id":1207,"body":"I know, right?","author":{"id":1,"email":"jack@example.com","name":"Jack Smith","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"created_at":"2012-11-27T19:58:21Z"}]

View File

@ -0,0 +1 @@
{"id":3,"code":"gitlab","name":"Gitlab","description":null,"path":"gitlab","default_branch":null,"owner":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"public":false,"issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":true,"wiki_enabled":true,"created_at":"2012-09-17T09:41:58Z"}

View File

@ -0,0 +1,13 @@
{
"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",
"committed_date": "2012-09-20T09:06:12+03:00",
"authored_date": "2012-09-20T09:06:12+03:00",
"parent_ids": [
"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
]
}

View File

@ -0,0 +1,10 @@
{
"diff": "--- a/doc/update/5.4-to-6.0.md\n+++ b/doc/update/5.4-to-6.0.md\n@@ -71,6 +71,8 @@\n sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production\n sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production\n \n+sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production\n+\n ```\n \n ### 6. Update config files",
"new_path": "doc/update/5.4-to-6.0.md",
"old_path": "doc/update/5.4-to-6.0.md",
"a_mode": null,
"b_mode": "100644",
"new_file": false,
"renamed_file": false,
"deleted_file": false
}

View File

@ -0,0 +1 @@
[{"id":"f7dd067490fe57505f7226c3b54d3127d2f7fd46","short_id":"f7dd067490f","title":"API: expose issues project id","author_name":"Nihad Abbasov","author_email":"narkoz.2008@gmail.com","created_at":"2012-07-25T04:22:21-07:00"},{"id":"949b1df930bedace1dbd755aaa4a82e8c451a616","short_id":"949b1df930b","title":"API: update docs","author_name":"Nihad Abbasov","author_email":"narkoz.2008@gmail.com","created_at":"2012-07-25T02:35:41-07:00"},{"id":"1b95c8bff351f6718ec31ac1de1e48c57bc95d44","short_id":"1b95c8bff35","title":"API: ability to get project by id","author_name":"Nihad Abbasov","author_email":"narkoz.2008@gmail.com","created_at":"2012-07-25T02:18:30-07:00"},{"id":"92d98f5a0c28bffd7b070cda190b07ab72667d58","short_id":"92d98f5a0c2","title":"Merge pull request #1118 from patthoyts/pt/ldap-missing-password","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","created_at":"2012-07-25T01:51:06-07:00"},{"id":"60d3e94874964a626f105d3598e1c122addcf43e","short_id":"60d3e948749","title":"Merge pull request #1122 from patthoyts/pt/missing-log","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","created_at":"2012-07-25T01:50:34-07:00"},{"id":"b683a71aa1230f17f9df47661c77dfeae27027de","short_id":"b683a71aa12","title":"Merge pull request #1135 from NARKOZ/api","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","created_at":"2012-07-25T01:48:00-07:00"},{"id":"fbb41100db35cf2def2c8b4d896b7015d56bd15b","short_id":"fbb41100db3","title":"update help section with issues API docs","author_name":"Nihad Abbasov","author_email":"narkoz.2008@gmail.com","created_at":"2012-07-24T05:52:43-07:00"},{"id":"eca823c1c7cef45cc18c6ab36d2327650c85bfc3","short_id":"eca823c1c7c","title":"Merge branch 'master' into api","author_name":"Nihad Abbasov","author_email":"narkoz.2008@gmail.com","created_at":"2012-07-24T05:46:36-07:00"},{"id":"024e0348904179a8dea81c01e27a5f014cf57499","short_id":"024e0348904","title":"update API docs","author_name":"Nihad Abbasov","author_email":"narkoz.2008@gmail.com","created_at":"2012-07-24T05:25:01-07:00"},{"id":"7b33d8cbcab3b0ee5789ec607455ab62130db69f","short_id":"7b33d8cbcab","title":"add issues API","author_name":"Nihad Abbasov","author_email":"narkoz.2008@gmail.com","created_at":"2012-07-24T05:19:51-07:00"},{"id":"6035ad7e1fe519d0c6a42731790183889e3ba31d","short_id":"6035ad7e1fe","title":"Create the githost.log file if necessary.","author_name":"Pat Thoyts","author_email":"patthoyts@users.sourceforge.net","created_at":"2012-07-21T07:32:04-07:00"},{"id":"a2d244ec062f3348f6cd1c5218c6097402c5f562","short_id":"a2d244ec062","title":"Handle LDAP missing credentials error with a flash message.","author_name":"Pat Thoyts","author_email":"patthoyts@users.sourceforge.net","created_at":"2012-07-21T01:04:05-07:00"},{"id":"8b7e404b5b6944e9c92cc270b2e5d0005781d49d","short_id":"8b7e404b5b6","title":"Up to 2.7.0","author_name":"randx","author_email":"dmitriy.zaporozhets@gmail.com","created_at":"2012-07-21T00:53:55-07:00"},{"id":"11721b0dbe82c35789be3e4fa8e14663934b2ff5","short_id":"11721b0dbe8","title":"Help section for system hooks completed","author_name":"randx","author_email":"dmitriy.zaporozhets@gmail.com","created_at":"2012-07-21T00:47:57-07:00"},{"id":"9c8a1e651716212cf50a623d98e03b8dbdb2c64a","short_id":"9c8a1e65171","title":"Fix system hook example","author_name":"randx","author_email":"dmitriy.zaporozhets@gmail.com","created_at":"2012-07-21T00:32:42-07:00"},{"id":"4261acda90ff4c61326d80cba026ae76e8551f8f","short_id":"4261acda90f","title":"move SSH keys tab closer to begining","author_name":"randx","author_email":"dmitriy.zaporozhets@gmail.com","created_at":"2012-07-21T00:27:09-07:00"},{"id":"a69fc5dd23bd502fd36892a80eec21a4c53891f8","short_id":"a69fc5dd23b","title":"Endless event loading for dsahboard","author_name":"randx","author_email":"dmitriy.zaporozhets@gmail.com","created_at":"2012-07-21T00:23:05-07:00"},{"id":"860fa1163a5fbdfec2bb01ff2d584351554dee29","short_id":"860fa1163a5","title":"Merge pull request #1117 from patthoyts/pt/user-form","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","created_at":"2012-07-20T14:23:49-07:00"},{"id":"787e5e94acf5e20280416c9fda105ef5b77576b3","short_id":"787e5e94acf","title":"Fix english on the edit user form.","author_name":"Pat Thoyts","author_email":"patthoyts@users.sourceforge.net","created_at":"2012-07-20T14:18:42-07:00"},{"id":"9267cb04b0b3fdf127899c4b7e636dc27fac06d3","short_id":"9267cb04b0b","title":"Merge branch 'refactoring_controllers' of dev.gitlabhq.com:gitlabhq","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","created_at":"2012-07-20T07:24:56-07:00"}]

View File

@ -0,0 +1,8 @@
{
"created_at": "2013-10-05T15:05:26Z",
"fingerprint": "5c:b5:e6:b0:f5:31:65:3f:a6:b5:59:86:32:cc:15:e1",
"id": 2,
"key": "ssh-rsa ...",
"updated_at": "2013-10-05T15:05:26Z",
"user_id": null
}

View File

@ -0,0 +1 @@
[{"title":null,"project_id":2,"action_name":"opened","target_id":null,"target_type":null,"author_id":1,"data":{"before":"ac0c1aa3898d6dea54d7868ea6f9c45fd5e30c59","after":"66350dbb62a221bc619b665aef3e1e7d3b306747","ref":"refs/heads/master","user_id":1,"user_name":"Administrator","project_id":2,"repository":{"name":"gitlab-ci","url":"git@demo.gitlab.com:gitlab/gitlab-ci.git","description":"Continuous integration server for gitlabhq | Coordinator","homepage":"http://demo.gitlab.com/gitlab/gitlab-ci"},"commits":[{"id":"8cf469b039931bab37bbf025e6b69287ea3cfb0e","message":"Modify screenshot\n\nSigned-off-by: Dmitriy Zaporozhets \u003Cdummy@gmail.com\u003E","timestamp":"2014-05-20T10:34:27+00:00","url":"http://demo.gitlab.com/gitlab/gitlab-ci/commit/8cf469b039931bab37bbf025e6b69287ea3cfb0e","author":{"name":"Dummy","email":"dummy@gmail.com"}},{"id":"66350dbb62a221bc619b665aef3e1e7d3b306747","message":"Edit some code\n\nSigned-off-by: Dmitriy Zaporozhets \u003Cdummy@gmail.com\u003E","timestamp":"2014-05-20T10:35:15+00:00","url":"http://demo.gitlab.com/gitlab/gitlab-ci/commit/66350dbb62a221bc619b665aef3e1e7d3b306747","author":{"name":"Dummy","email":"dummy@gmail.com"}}],"total_commits_count":2},"target_title":null,"created_at":"2014-05-20T10:35:26.240Z"},{"title":null,"project_id":2,"action_name":"opened","target_id":2,"target_type":"MergeRequest","author_id":1,"data":null,"target_title":" Morbi et cursus leo. Sed eget vestibulum sapien","created_at":"2014-05-20T10:24:11.917Z"}]

View File

@ -0,0 +1 @@
{"id":1,"code":"brute","name":"Brute","description":null,"path":"brute","default_branch":null,"owner":{"id":1,"email":"john@example.com","name":"John Owner","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"private":true,"issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":true,"wiki_enabled":true,"created_at":"2012-09-17T09:41:56Z"}

View File

@ -0,0 +1 @@
{"created_at":"2013-07-03T13:51:48Z","forked_from_project_id":24,"forked_to_project_id":42,"id":1,"updated_at":"2013-07-03T13:51:48Z"}

View File

@ -0,0 +1 @@
{"id":1,"url":"https://api.example.net/v1/webhooks/ci"}

View File

@ -0,0 +1 @@
[{"id":1,"url":"https://api.example.net/v1/webhooks/ci"}]

View File

@ -0,0 +1 @@
[{"id":36,"project_id":3,"title":"Eos ut modi et laudantium quasi porro voluptas sed.","description":null,"labels":[],"milestone":null,"assignee":{"id":5,"email":"aliza_stark@schmeler.info","name":"Michale Von","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"author":{"id":5,"email":"aliza_stark@schmeler.info","name":"Michale Von","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":35,"project_id":3,"title":"Ducimus illo in iure voluptatem dolores labore.","description":null,"labels":[],"milestone":null,"assignee":{"id":4,"email":"nicole@mertz.com","name":"Felipe Davis","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"author":{"id":4,"email":"nicole@mertz.com","name":"Felipe Davis","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":34,"project_id":3,"title":"Rem tempora voluptatum atque eum sit nihil neque.","description":null,"labels":[],"milestone":null,"assignee":{"id":3,"email":"wilma@mayerblanda.ca","name":"Beatrice Jewess","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"author":{"id":3,"email":"wilma@mayerblanda.ca","name":"Beatrice Jewess","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":33,"project_id":3,"title":"Beatae possimus nostrum nihil reiciendis laboriosam nihil delectus alias accusantium dolor unde.","description":null,"labels":[],"milestone":null,"assignee":{"id":2,"email":"jack@example.com","name":"Jack Smith","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"author":{"id":2,"email":"jack@example.com","name":"Jack Smith","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":32,"project_id":3,"title":"Deserunt tenetur impedit est beatae voluptas voluptas quaerat quisquam.","description":null,"labels":[],"milestone":null,"assignee":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":16,"project_id":3,"title":"Numquam earum aut laudantium reprehenderit voluptatem aut.","description":null,"labels":[],"milestone":null,"assignee":{"id":5,"email":"aliza_stark@schmeler.info","name":"Michale Von","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"author":{"id":5,"email":"aliza_stark@schmeler.info","name":"Michale Von","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":15,"project_id":3,"title":"Qui veritatis voluptas fuga voluptate voluptas cupiditate.","description":null,"labels":[],"milestone":null,"assignee":{"id":4,"email":"nicole@mertz.com","name":"Felipe Davis","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"author":{"id":4,"email":"nicole@mertz.com","name":"Felipe Davis","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":14,"project_id":3,"title":"In assumenda et ipsa qui debitis voluptatem incidunt.","description":null,"labels":[],"milestone":null,"assignee":{"id":3,"email":"wilma@mayerblanda.ca","name":"Beatrice Jewess","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"author":{"id":3,"email":"wilma@mayerblanda.ca","name":"Beatrice Jewess","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":13,"project_id":3,"title":"Illo eveniet consequatur enim iste provident facilis rerum voluptatem et architecto aut.","description":null,"labels":[],"milestone":null,"assignee":{"id":2,"email":"jack@example.com","name":"Jack Smith","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"author":{"id":2,"email":"jack@example.com","name":"Jack Smith","blocked":false,"created_at":"2012-09-17T09:42:03Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"},{"id":12,"project_id":3,"title":"Veniam et tempore quidem eum reprehenderit cupiditate non aut velit eaque.","description":null,"labels":[],"milestone":null,"assignee":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"closed":false,"updated_at":"2012-09-17T09:42:20Z","created_at":"2012-09-17T09:42:20Z"}]

Some files were not shown because too many files have changed in this diff Show More