diff --git a/Gemfile b/Gemfile index cf6e9f773..87cd6dba2 100644 --- a/Gemfile +++ b/Gemfile @@ -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' @@ -45,7 +46,7 @@ group :development, :test do gem 'pry-stack_explorer' if RUBY_PLATFORM =~ /darwin/ gem 'puma' - end + end end gem 'rspec-rails', '~> 3.0' diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7632cf591..bb2138062 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -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 diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index f2f10d5b6..742a02337 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -29,12 +29,12 @@ class RepositoriesController < ApplicationController menu_item :repository menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers] default_search_scope :changesets - + 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 ] @@ -42,7 +42,7 @@ class RepositoriesController < ApplicationController include RepositoriesHelper helper :project_score #@root_path = RepositoriesHelper::ROOT_PATH - + rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed def new @@ -62,8 +62,8 @@ class RepositoriesController < ApplicationController end end - - + + def newrepo scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first @repository = Repository.factory(scm) @@ -76,23 +76,23 @@ class RepositoriesController < ApplicationController render :layout => 'base_projects' end end - + def fork @repository_url = params[:repository_url] - + # @repository.url # system "htpasswd -mb "+@root_path+"user.passwd "+params[:repository][:identifier]+" "+@upasswd # system "echo -e '"+params[:project_id]+"-"+params[:repository][:identifier]+"-write:"+ - # " "+params[:repository][:identifier]+"' >> "+@root_path+"group.passwd" - system "git clone --bare "+@repository_url + # " "+params[:repository][:identifier]+"' >> "+@root_path+"group.passwd" + system "git clone --bare "+@repository_url # system "mv "+@project_path+"/hooks/post-update{.sample,}" # system "chmod a+x "+@project_path+"/hooks/post-update" # system "."+@project_path+"/hooks/post-update" # system "echo -e 'Allow from all \n Order Deny,Allow \n "+ - # " \n"+ - # "Require group "+params[:project_id]+"-"+params[:repository][:identifier]+"-write \n "+ - # " \n ' >>"+ - # @project_path+"/.htaccess" + # " \n"+ + # "Require group "+params[:project_id]+"-"+params[:repository][:identifier]+"-write \n "+ + # " \n ' >>"+ + # @project_path+"/.htaccess" flash[:notice] = l(:label_notice_fork_successed) @repositories = @project.repositories render :action => 'show', :layout => 'base_projects' @@ -115,77 +115,36 @@ 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] - if attrs[:attrs_extra].keys.any? - @repository.merge_extra_info(attrs[:attrs_extra]) - end - @repository.project = @project - if request.post? && @repository.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!="")&¶ms[: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 "+ - " \n"+ - "Require group "+@repo_name+"-write \n "+ - " \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 - - + attrs = pickup_extra_info + @repository = Repository.factory('Git') + @repository.safe_attributes = params[:repository] + if attrs[:attrs_extra].keys.any? + @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',:repository_error_message=>@repository.errors.full_messages) end end - + def edit 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 - - + 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 @@ -276,15 +251,22 @@ update @entries ? render(:partial => 'dir_list_content') : render(:nothing => true) else #Modified by young - # (show_error_not_found; return) unless @entries + # (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 - @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 + 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 @@ -310,10 +292,10 @@ update per_page_option, params['page'] @changesets = @repository.changesets. - limit(@changeset_pages.per_page). - offset(@changeset_pages.offset). - includes(:user, :repository, :parents). - all + limit(@changeset_pages.per_page). + offset(@changeset_pages.offset). + includes(:user, :repository, :parents). + all respond_to do |format| format.html { render :layout => 'base_projects' } @@ -327,6 +309,7 @@ update def entry entry_and_raw(false) + render :layout => 'base_projects' end def entry_and_raw(is_raw) @@ -339,8 +322,8 @@ update @content = @repository.cat(@path, @rev) (show_error_not_found; return) unless @content if is_raw || - (@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) || - ! is_entry_text_data?(@content, @path) + (@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) || + ! is_entry_text_data?(@content, @path) # Force the download send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) } send_type = Redmine::MimeType.of(@path) @@ -423,8 +406,8 @@ update filename = "changeset_r#{@rev}" filename << "_r#{@rev_to}" if @rev_to send_data @diff.join, :filename => "#{filename}.diff", - :type => 'text/x-patch', - :disposition => 'attachment' + :type => 'text/x-patch', + :disposition => 'attachment' else @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline' @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type) @@ -435,7 +418,7 @@ update User.current.preference.save end @cache_key = "repositories/diff/#{@repository.id}/" + - Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}") + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}") unless read_fragment(@cache_key) @diff = @repository.diff(@path, @rev, @rev_to) unless @diff @@ -462,16 +445,16 @@ update def graph data = nil case params[:graph] - when "commits_per_month" - data = graph_commits_per_month(@repository) - when "commits_per_author" - data = graph_commits_per_author(@repository) - when "author_commits_per_month" - data = graph_author_commits_per_month(@repository) - when "author_commits_six_month" - data = author_commits_six_month(@repository) - when "author_code_six_months" - data = author_code_six_month(@repository) + when "commits_per_month" + data = graph_commits_per_month(@repository) + when "commits_per_author" + data = graph_commits_per_author(@repository) + when "author_commits_per_month" + data = graph_author_commits_per_month(@repository) + when "author_commits_six_month" + data = author_commits_six_month(@repository) + when "author_code_six_months" + data = author_code_six_month(@repository) end if data headers["Content-Type"] = "image/svg+xml" @@ -551,14 +534,14 @@ update @date_from = @date_to << 11 @date_from = Date.civil(@date_from.year, @date_from.month, 1) commits_by_day = Changeset.count( - :all, :group => :commit_date, - :conditions => ["repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to]) + :all, :group => :commit_date, + :conditions => ["repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to]) commits_by_month = [0] * 12 commits_by_day.each {|c| commits_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last } changes_by_day = Change.count( - :all, :group => :commit_date, :include => :changeset, - :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to]) + :all, :group => :commit_date, :include => :changeset, + :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to]) changes_by_month = [0] * 12 changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last } @@ -566,26 +549,26 @@ update 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)} graph = SVG::Graph::Bar.new( - :height => 300, - :width => 600, - :fields => fields.reverse, - :stack => :side, - :scale_integers => true, - :step_x_labels => 2, - :show_data_values => true, - :graph_title => l(:label_commits_per_month), - :show_graph_title => true + :height => 300, + :width => 600, + :fields => fields.reverse, + :stack => :side, + :scale_integers => true, + :step_x_labels => 2, + :show_data_values => true, + :graph_title => l(:label_commits_per_month), + :show_graph_title => true ) # 具状图 graph.add_data( - :data => commits_by_month[0..11].reverse, - :title => l(:label_revision_plural) + :data => commits_by_month[0..11].reverse, + :title => l(:label_revision_plural) ) graph.add_data( - :data => changes_by_month[0..11].reverse, - :title => l(:label_change_plural) + :data => changes_by_month[0..11].reverse, + :title => l(:label_change_plural) ) graph.burn @@ -610,23 +593,23 @@ update fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') } graph = SVG::Graph::BarHorizontal.new( - :height => 400, - :width => 600, - :fields => fields, - :stack => :side, - :scale_integers => true, - :show_data_values => true, - :rotate_y_labels => false, - :graph_title => l(:label_commits_per_author), - :show_graph_title => true + :height => 400, + :width => 600, + :fields => fields, + :stack => :side, + :scale_integers => true, + :show_data_values => true, + :rotate_y_labels => false, + :graph_title => l(:label_commits_per_author), + :show_graph_title => true ) graph.add_data( - :data => commits_data, - :title => l(:label_revision_plural) + :data => commits_data, + :title => l(:label_revision_plural) ) graph.add_data( - :data => changes_data, - :title => l(:label_change_plural) + :data => changes_data, + :title => l(:label_change_plural) ) graph.burn end @@ -637,7 +620,7 @@ update @date_from = @date_to << 12 @date_from = Date.civil(@date_from.year, @date_from.month, @date_from.day) commits_by_author = Changeset.count(:all, :group => :committer, - :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to]) + :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to]) commits_by_author = commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}.last(25) fields = commits_by_author.collect {|r| r.first} diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 710b7488f..ba99fe454 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -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...') diff --git a/app/models/member.rb b/app/models/member.rb index 655c79895..ed7ee81e6 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -34,6 +34,7 @@ class Member < ActiveRecord::Base after_destroy :delete_ivite_list + def role end diff --git a/app/models/member_role.rb b/app/models/member_role.rb index 67122a636..4d493cb27 100644 --- a/app/models/member_role.rb +++ b/app/models/member_role.rb @@ -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 diff --git a/app/models/project.rb b/app/models/project.rb index fcc96c404..fd1012fa0 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -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 diff --git a/app/models/repository.rb b/app/models/repository.rb index 5680f05a7..94b7905c6 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -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) diff --git a/app/models/repository/gitlab.rb b/app/models/repository/gitlab.rb new file mode 100644 index 000000000..5ec725660 --- /dev/null +++ b/app/models/repository/gitlab.rb @@ -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 diff --git a/app/models/role.rb b/app/models/role.rb index f363b52bf..8bf5ebc05 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -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? diff --git a/app/models/user.rb b/app/models/user.rb index 7aaae3492..b75b67a1a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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 @@ -358,7 +358,7 @@ class User < Principal name end ## end - + #added by nie def count_new_journal_reply count = self.journal_reply.count @@ -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 diff --git a/app/views/common/_file.html.erb b/app/views/common/_file.html.erb index d4c0875c2..97443beea 100644 --- a/app/views/common/_file.html.erb +++ b/app/views/common/_file.html.erb @@ -8,7 +8,7 @@ <%= line_num %> -
<%= line.html_safe %>
+
<%= line.html_safe %>
<% line_num += 1 %> diff --git a/app/views/layouts/base_projects.html.erb b/app/views/layouts/base_projects.html.erb index 02085b95a..0f622d831 100644 --- a/app/views/layouts/base_projects.html.erb +++ b/app/views/layouts/base_projects.html.erb @@ -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 %> diff --git a/app/views/projects/_development_group.html.erb b/app/views/projects/_development_group.html.erb index 915637ec5..1770291f5 100644 --- a/app/views/projects/_development_group.html.erb +++ b/app/views/projects/_development_group.html.erb @@ -41,7 +41,7 @@ <%# --版本库被设置成私有、module中设置不显示、没有创建版本库 三种情况不显示-- %> <% if visible_repository?(@project) %> <% end %> diff --git a/app/views/projects/_new_join.html.erb b/app/views/projects/_new_join.html.erb index c823f5ed0..c406ac2e6 100644 --- a/app/views/projects/_new_join.html.erb +++ b/app/views/projects/_new_join.html.erb @@ -18,6 +18,13 @@ @@ -33,7 +40,7 @@ <%= text_field_tag 'course_password', nil, :style=>'width:300px;'%>
- + 确  定 diff --git a/app/views/projects/settings/_new_repositories.html.erb b/app/views/projects/settings/_new_repositories.html.erb index 0859e5e19..bfb87b16a 100644 --- a/app/views/projects/settings/_new_repositories.html.erb +++ b/app/views/projects/settings/_new_repositories.html.erb @@ -73,13 +73,6 @@ <%= l(:text_scm_command_not_available) %> <% end %> - - <% unless judge_main_repository(@project) %> -
  • - - <%= f.check_box :is_default, :label => "", :no_label => true %>

    -
  • - <% end %>
  • @@ -89,11 +82,6 @@ <%=l(:text_length_between,:min=>1,:max=>254)< <% end %>
  • -
  • - - <%= f.password_field :upassword, :label=> "", :no_label => true%> - <%= l(:label_upassword_info)%> -
  • <%=l(:button_save)%> diff --git a/app/views/repositories/_breadcrumbs.html.erb b/app/views/repositories/_breadcrumbs.html.erb index db570d948..84111afeb 100644 --- a/app/views/repositories/_breadcrumbs.html.erb +++ b/app/views/repositories/_breadcrumbs.html.erb @@ -1,32 +1,13 @@ -<%= 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) -%> -

    <%= "@ #{h rev_text}" unless rev_text.blank? %>

    +
    + <%= link_to @repository.identifier.present? ? h(@repository.identifier) : 'root', + {:action => 'show', :id => @project, + :repository_id => @repository.identifier_param, + :path => nil, :rev => @rev }, + :class => "repository-title-dec" + %> + / + <%=link_to @project.owner, user_path(@project.owner), :class => "repository-title-dec" %> + +
    <% html_title(with_leading_slash(path)) -%> diff --git a/app/views/repositories/_dir_list.html.erb b/app/views/repositories/_dir_list.html.erb index 26ba05bb6..771d4d1ec 100644 --- a/app/views/repositories/_dir_list.html.erb +++ b/app/views/repositories/_dir_list.html.erb @@ -1,17 +1,6 @@
    - - - - -<% if @repository.report_last_commit %> - - - - -<% end %> - - + <%= render :partial => 'dir_list_content' %> diff --git a/app/views/repositories/_dir_list_content.html.erb b/app/views/repositories/_dir_list_content.html.erb index e595a77dd..abee097c5 100644 --- a/app/views/repositories/_dir_list_content.html.erb +++ b/app/views/repositories/_dir_list_content.html.erb @@ -7,6 +7,7 @@ + <% if @repository.report_last_commit %> diff --git a/app/views/repositories/_form.html.erb b/app/views/repositories/_form.html.erb index bc5a060db..8c169d7be 100644 --- a/app/views/repositories/_form.html.erb +++ b/app/views/repositories/_form.html.erb @@ -31,12 +31,7 @@ <%= f.text_field :login, :size => 30 %>

    -

    - <%= 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]';" %> -

    +

    diff --git a/app/views/repositories/_form_create.html.erb b/app/views/repositories/_form_create.html.erb index d8c30143e..11dce9cd4 100644 --- a/app/views/repositories/_form_create.html.erb +++ b/app/views/repositories/_form_create.html.erb @@ -26,7 +26,6 @@ border:none <%= l(:text_scm_command_not_available) %> <% end %>

    -

    <%= f.check_box :is_default, :label => :field_repository_is_default %>

    @@ -36,8 +35,6 @@ border:none <%= l(:text_repository_identifier_info).html_safe %> <% end %>

    -

    <%= f.password_field :upassword, :required =>true, :label=> :field_password %> - <%= l(:label_upassword_info)%>

    <%= submit_tag(@repository.new_record? ? l(:button_create) : l(:button_save)) %> diff --git a/app/views/repositories/_navigation.html.erb b/app/views/repositories/_navigation.html.erb index d8ce218eb..c3e154f17 100644 --- a/app/views/repositories/_navigation.html.erb +++ b/app/views/repositories/_navigation.html.erb @@ -1,34 +1,33 @@ -<% content_for :header_tags do %> - <%= javascript_include_tag 'repository_navigation' %> -<% end %> - -<%= link_to l(:label_statistics), + +<%#= link_to l(:label_statistics), {:action => 'stats', :id => @project, :repository_id => @repository.identifier_param}, :class => 'mt3 c_blue fl' if @repository.supports_all_revisions? %> +

    + <% content_for :header_tags do %> + <%= javascript_include_tag 'repository_navigation' %> + <% end %> -<%= 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 -%> - - <% if !@repository.branches.nil? && @repository.branches.length > 0 -%> - | <%= 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' %> - <% end -%> - <% if @repository.supports_all_revisions? %> - | <%= l(:label_revision) %>: - <%= text_field_tag 'rev', @rev, :size => 8 %> - <% end %> -<% end -%> + <%= 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'}) do -%> + + <% if !@repository.branches.nil? && @repository.branches.length > 0 -%> + <%= l(:label_branch) %>: + <%= select_tag :branch, options_for_select([''] + @repository.branches, @rev), :id => 'branch' %> + <% end -%> + + <% if !@repository.tags.nil? && @repository.tags.length > 0 -%> + <%= select_tag :tag, options_for_select([''] + @repository.tags, @rev), :id => 'tag', :style=>" display:none" %> + <% end -%> + + <% if @repository.supports_all_revisions? %> + <%= hidden_field_tag 'rev', @rev, :size => 8 %> + <% end %> + <% end -%> + +
    diff --git a/app/views/repositories/_summary.html.erb b/app/views/repositories/_summary.html.erb new file mode 100644 index 000000000..3bd86cd45 --- /dev/null +++ b/app/views/repositories/_summary.html.erb @@ -0,0 +1,37 @@ +
    + +
    +
    + + + +
    +
    + +
    \ No newline at end of file diff --git a/app/views/repositories/changes.html.erb b/app/views/repositories/changes.html.erb index 302c839ba..f4037cb36 100644 --- a/app/views/repositories/changes.html.erb +++ b/app/views/repositories/changes.html.erb @@ -1,18 +1,20 @@ <%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %> -
    - <%= render :partial => 'navigation' %> +
    +

    <%= render :partial => 'breadcrumbs', :locals => {:path => @path, :kind => 'dir', :revision => @rev} %>

    -

    <%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %>

    +
    + <%= render :partial => 'navigation' %> +
    +
    <%= render :partial => 'link_to_functions' %> <%= render_properties(@properties) %> -
    -<%= render(:partial => 'revisions', - :locals => {:project => @project, :path => @path, :revisions => @changesets, :entry => @entry }) unless @changesets.empty? %> +
    + <%= render(:partial => 'revisions', :locals => {:project => @project, :path => @path, :revisions => @changesets, :entry => @entry }) unless @changesets.empty? %>
    <% content_for :header_tags do %> <%= stylesheet_link_tag "scm" %> diff --git a/app/views/repositories/committers.html.erb b/app/views/repositories/committers.html.erb index ccb037e09..93350889d 100644 --- a/app/views/repositories/committers.html.erb +++ b/app/views/repositories/committers.html.erb @@ -1,4 +1,8 @@ -

    <%= l(:label_repository) %>

    +
    +
    +

    <%= render :partial => 'breadcrumbs', :locals => {:path => @path, :kind => 'dir', :revision => @rev} %>

    +
    +
    <%= simple_format(l(:text_repository_usernames_mapping)) %> diff --git a/app/views/repositories/diff.html.erb b/app/views/repositories/diff.html.erb index a3afebfff..4e40bd5e8 100644 --- a/app/views/repositories/diff.html.erb +++ b/app/views/repositories/diff.html.erb @@ -1,4 +1,7 @@ -

    <%= l(:label_revision) %> <%= @diff_format_revisions %> <%=h @path %>

    +
    +

    <%= render :partial => 'breadcrumbs', :locals => {:path => @path, :kind => 'dir', :revision => @rev} %>

    +
    +

    <%= l(:label_revision_path) %> :<%=h @path %>

    <%= form_tag({:action => 'diff', :id => @project, diff --git a/app/views/repositories/entry.html.erb b/app/views/repositories/entry.html.erb index 5aea99dcc..bb2fa6dae 100644 --- a/app/views/repositories/entry.html.erb +++ b/app/views/repositories/entry.html.erb @@ -1,15 +1,17 @@ <%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %> +
    +
    + <%= render :partial => 'navigation' %> +
    -
    - <%= render :partial => 'navigation' %> +

    <%= l(:label_revision_path) %> :<%= @path %>

    + + <%= render :partial => 'link_to_functions' %> + + <%= render :partial => 'common/file', :locals => {:filename => @path, :content => @content} %> + + <% content_for :header_tags do %> + <%= stylesheet_link_tag "scm" %> + <% end %>
    -

    <%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => 'file', :revision => @rev } %>

    - -<%= render :partial => 'link_to_functions' %> - -<%= render :partial => 'common/file', :locals => {:filename => @path, :content => @content} %> - -<% content_for :header_tags do %> -<%= stylesheet_link_tag "scm" %> -<% end %> diff --git a/app/views/repositories/show.html.erb b/app/views/repositories/show.html.erb index 28a260178..a1dd44cb7 100644 --- a/app/views/repositories/show.html.erb +++ b/app/views/repositories/show.html.erb @@ -1,83 +1,74 @@ <%= call_hook(:view_repositories_show_contextual, {:repository => @repository, :project => @project}) %>
    -

    版本库

    +

    <%= render :partial => 'breadcrumbs', :locals => {:path => @path, :kind => 'dir', :revision => @rev} %>

    -
    - <%= render :partial => 'breadcrumbs', - :locals => {:path => @path, :kind => 'dir', :revision => @rev} %> - <%= render :partial => 'navigation' %> -
    - -
    -
    -

    - <% if @repository.type.to_s=="Repository::Git" %> - <%= @repos_url %> - <% else %> - <%= h @repository.url %> - <% end %> -

    + <% if @entries.nil? %> + <%# 未提交代码提示 %> +
    + <% if @entries.nil? && authorize_for('repositories', 'browse') %> +
    + 该版本库还没有上传代码! +
    + <% end %> + <% if @repository.type.to_s=="Repository::Gitlab" %> + 版本库地址:<%= @repos_url %> + <% else %> + 版本库地址:<%= h @repository.url %> + <% end %> + + +
    +
    + <% else %> + <%= render :partial => 'navigation' %> +
    克隆网址:
    + + + +
    Fork 0
    +
    +
    + <% if @changesets && !@changesets.empty? %> + <%= image_tag(url_to_avatar(@changesets_latest_coimmit.user), :width => "25", :height => "25", :class => "fl portraitRadius mt2 ml4 mr5") %> +
    <%=link_to @changesets_latest_coimmit.user, user_path(@changesets_latest_coimmit.user) %>
    +
    提交于<%= time_tag(@changesets_latest_coimmit.committed_on) %>:
    +
    <%= @changesets_latest_coimmit.comments %>
    +
    + <% end %> + + <%= @repository.branches.count %> 个分支 + -

    - (<%= 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(' | ').html_safe %>) -

    -
    + + <%=link_to @changesets_count, {:action => 'changes', :path => to_path_param(@path), :id => @project, :repository_id => @repository.identifier_param, :rev => @rev} %> 提交 + +
    + <% 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? %> -

    - <%= l(:label_latest_revision_plural) %> -

    - <%= render :partial => 'revisions', - :locals => {:project => @project, :path => @path, - :revisions => @changesets, :entry => nil} %> - <%# end %> +<%= render_properties(@properties) %> -

    +<% 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 %> -

    - <% 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 %> - <% end %> + <% if @repository.supports_all_revisions? && @path.blank? %> + <%= link_to l(:label_view_all_revisions_commits), :action => 'revisions', :id => @project, :repository_id => @repository.identifier_param %> + <% end %> | + <% end %> <% end %> - - -

    点击查看如何提交代码

    -
    +如何提交代码 <% content_for :header_tags do %> <%= stylesheet_link_tag "scm" %> diff --git a/app/views/repositories/to_gitlab.html.erb b/app/views/repositories/to_gitlab.html.erb new file mode 100644 index 000000000..d06c087ed --- /dev/null +++ b/app/views/repositories/to_gitlab.html.erb @@ -0,0 +1,12 @@ +
    +

    + <%= l(:label_repository_migrate_dec) %> +

    +<%= form_for(@repository, url: to_gitlab_project_repository_path(@project, @repository)) do |f| %> + + +<% end %> + + <%= l(:label_repository_name_dec) %> + +
    \ No newline at end of file diff --git a/config/application.rb b/config/application.rb index 83ba21b05..90cc299c6 100644 --- a/config/application.rb +++ b/config/application.rb @@ -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 diff --git a/config/initializers/gitlab_config.rb b/config/initializers/gitlab_config.rb new file mode 100644 index 000000000..75edc8eff --- /dev/null +++ b/config/initializers/gitlab_config.rb @@ -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 diff --git a/config/locales/en.yml b/config/locales/en.yml index 2071529f0..2bed45103 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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 diff --git a/config/locales/projects/zh.yml b/config/locales/projects/zh.yml index dbce93219..bd31c4d79 100644 --- a/config/locales/projects/zh.yml +++ b/config/locales/projects/zh.yml @@ -322,6 +322,7 @@ zh: label_latest_revision_plural: 最近的修订版本 label_view_revisions: 查看修订 label_view_all_revisions: 查看所有修订 + label_view_all_revisions_commits: 查看所有提交记录 # # 项目托管平台 # @@ -440,4 +441,8 @@ zh: # field_sharing: 共享 label_title_code_review: 代码评审 - label_home_non_project: 您还没有创建项目,您可以查看系统的其它项目! \ No newline at end of file + label_home_non_project: 您还没有创建项目,您可以查看系统的其它项目! + + # 版本库迁移 + label_repository_migrate_dec: 注意:Trustie版本库近期进行了一次大的改造,历史版本需要转换成新的版本,输入新的版本库名,即可完成转换。 转换过程可能需要等待一段时间。 + label_repository_name_dec: 版本库名仅小写字母(a-z)、数字、破折号(-)和下划线(_)可以使用,长度必须在 1 到 254 个字符之间,一旦保存,标识无法修改。 \ No newline at end of file diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 5efe4a2cc..a7274c3c2 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -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} 个子页面和下级页面。您想进行那种操作? diff --git a/config/routes.rb b/config/routes.rb index fbce06e02..04dd05271 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 diff --git a/db/migrate/20150712063406_add_gitlab_user_id_to_users.rb b/db/migrate/20150712063406_add_gitlab_user_id_to_users.rb new file mode 100644 index 000000000..f6e2d60cd --- /dev/null +++ b/db/migrate/20150712063406_add_gitlab_user_id_to_users.rb @@ -0,0 +1,5 @@ +class AddGitlabUserIdToUsers < ActiveRecord::Migration + def change + add_column :users, :gid, :integer + end +end diff --git a/db/migrate/20151013023237_add_gpid_to_project.rb b/db/migrate/20151013023237_add_gpid_to_project.rb new file mode 100644 index 000000000..bfa535253 --- /dev/null +++ b/db/migrate/20151013023237_add_gpid_to_project.rb @@ -0,0 +1,5 @@ +class AddGpidToProject < ActiveRecord::Migration + def change + add_column :projects, :gpid, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index b18ffb544..7dab1b3a7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,1759 +1,1754 @@ -# encoding: UTF-8 -# This file is auto-generated from the current state of the database. Instead -# of editing this file, please use the migrations feature of Active Record to -# incrementally modify your database, and then regenerate this schema definition. -# -# Note that this schema.rb definition is the authoritative source for your -# database schema. If you need to create the application database on another -# system, you should be using db:schema:load, not running all the migrations -# from scratch. The latter is a flawed and unsustainable approach (the more migrations -# you'll amass, the slower it'll run and the greater likelihood for issues). -# -# It's strongly recommended to check this file into your version control system. - -ActiveRecord::Schema.define(:version => 20151022071804) do - - create_table "activities", :force => true do |t| - t.integer "act_id", :null => false - t.string "act_type", :null => false - t.integer "user_id", :null => false - t.integer "activity_container_id" - t.string "activity_container_type", :default => "" - t.datetime "created_at" - end - - add_index "activities", ["act_id", "act_type"], :name => "index_activities_on_act_id_and_act_type" - add_index "activities", ["user_id", "act_type"], :name => "index_activities_on_user_id_and_act_type" - add_index "activities", ["user_id"], :name => "index_activities_on_user_id" - - create_table "activity_notifies", :force => true do |t| - t.integer "activity_container_id" - t.string "activity_container_type" - t.integer "activity_id" - t.string "activity_type" - t.integer "notify_to" - t.datetime "created_on" - t.integer "is_read" - end - - add_index "activity_notifies", ["activity_container_id", "activity_container_type"], :name => "index_an_activity_container_id" - add_index "activity_notifies", ["created_on"], :name => "index_an_created_on" - add_index "activity_notifies", ["notify_to"], :name => "index_an_notify_to" - - create_table "api_keys", :force => true do |t| - t.string "access_token" - t.datetime "expires_at" - t.integer "user_id" - t.boolean "active", :default => true - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - add_index "api_keys", ["access_token"], :name => "index_api_keys_on_access_token" - add_index "api_keys", ["user_id"], :name => "index_api_keys_on_user_id" - - create_table "applied_projects", :force => true do |t| - t.integer "project_id", :null => false - t.integer "user_id", :null => false - end - - create_table "apply_project_masters", :force => true do |t| - t.integer "user_id" - t.string "apply_type" - t.integer "apply_id" - t.integer "status" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "attachments", :force => true do |t| - t.integer "container_id" - t.string "container_type", :limit => 30 - t.string "filename", :default => "", :null => false - t.string "disk_filename", :default => "", :null => false - t.integer "filesize", :default => 0, :null => false - t.string "content_type", :default => "" - t.string "digest", :limit => 40, :default => "", :null => false - t.integer "downloads", :default => 0, :null => false - t.integer "author_id", :default => 0, :null => false - t.datetime "created_on" - t.string "description" - t.string "disk_directory" - t.integer "attachtype", :default => 1 - t.integer "is_public", :default => 1 - t.integer "copy_from" - t.integer "quotes" - end - - add_index "attachments", ["author_id"], :name => "index_attachments_on_author_id" - add_index "attachments", ["container_id", "container_type"], :name => "index_attachments_on_container_id_and_container_type" - add_index "attachments", ["created_on"], :name => "index_attachments_on_created_on" - - create_table "attachmentstypes", :force => true do |t| - t.integer "typeId", :null => false - t.string "typeName", :limit => 50 - end - - create_table "auth_sources", :force => true do |t| - t.string "type", :limit => 30, :default => "", :null => false - t.string "name", :limit => 60, :default => "", :null => false - t.string "host", :limit => 60 - t.integer "port" - t.string "account" - t.string "account_password", :default => "" - t.string "base_dn" - t.string "attr_login", :limit => 30 - t.string "attr_firstname", :limit => 30 - t.string "attr_lastname", :limit => 30 - t.string "attr_mail", :limit => 30 - t.boolean "onthefly_register", :default => false, :null => false - t.boolean "tls", :default => false, :null => false - t.string "filter" - t.integer "timeout" - end - - add_index "auth_sources", ["id", "type"], :name => "index_auth_sources_on_id_and_type" - - create_table "biding_projects", :force => true do |t| - t.integer "project_id" - t.integer "bid_id" - t.integer "user_id" - t.string "description" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "reward" - end - - create_table "bids", :force => true do |t| - t.string "name" - t.string "budget", :null => false - t.integer "author_id" - t.date "deadline" - t.text "description" - t.datetime "created_on", :null => false - t.datetime "updated_on", :null => false - t.integer "commit" - t.integer "reward_type" - t.integer "homework_type" - t.integer "parent_id" - t.string "password" - t.integer "is_evaluation" - t.integer "proportion", :default => 60 - t.integer "comment_status", :default => 0 - t.integer "evaluation_num", :default => 3 - t.integer "open_anonymous_evaluation", :default => 1 - end - - create_table "blog_comments", :force => true do |t| - t.integer "blog_id", :null => false - t.integer "parent_id" - t.string "title", :default => "", :null => false - t.text "content" - t.integer "author_id" - t.integer "comments_count", :default => 0, :null => false - t.integer "last_comment_id" - t.datetime "created_on", :null => false - t.datetime "updated_on", :null => false - t.boolean "locked", :default => false - t.integer "sticky", :default => 0 - t.integer "reply_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "blogs", :force => true do |t| - t.string "name", :default => "", :null => false - t.text "description" - t.integer "position", :default => 1 - t.integer "article_count", :default => 0, :null => false - t.integer "comments_count", :default => 0, :null => false - t.integer "last_comments_id" - t.integer "parent_id" - t.integer "author_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "boards", :force => true do |t| - t.integer "project_id", :null => false - t.string "name", :default => "", :null => false - t.string "description" - t.integer "position", :default => 1 - t.integer "topics_count", :default => 0, :null => false - t.integer "messages_count", :default => 0, :null => false - t.integer "last_message_id" - t.integer "parent_id" - t.integer "course_id" - end - - add_index "boards", ["last_message_id"], :name => "index_boards_on_last_message_id" - add_index "boards", ["project_id"], :name => "boards_project_id" - - create_table "bug_to_osps", :force => true do |t| - t.integer "osp_id" - t.integer "relative_memo_id" - t.string "description" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "changes", :force => true do |t| - t.integer "changeset_id", :null => false - t.string "action", :limit => 1, :default => "", :null => false - t.text "path", :null => false - t.text "from_path" - t.string "from_revision" - t.string "revision" - t.string "branch" - end - - add_index "changes", ["changeset_id"], :name => "changesets_changeset_id" - - create_table "changeset_parents", :id => false, :force => true do |t| - t.integer "changeset_id", :null => false - t.integer "parent_id", :null => false - end - - add_index "changeset_parents", ["changeset_id"], :name => "changeset_parents_changeset_ids" - add_index "changeset_parents", ["parent_id"], :name => "changeset_parents_parent_ids" - - create_table "changesets", :force => true do |t| - t.integer "repository_id", :null => false - t.string "revision", :null => false - t.string "committer" - t.datetime "committed_on", :null => false - t.text "comments" - t.date "commit_date" - t.string "scmid" - t.integer "user_id" - end - - add_index "changesets", ["committed_on"], :name => "index_changesets_on_committed_on" - add_index "changesets", ["repository_id", "revision"], :name => "changesets_repos_rev", :unique => true - add_index "changesets", ["repository_id", "scmid"], :name => "changesets_repos_scmid" - add_index "changesets", ["repository_id"], :name => "index_changesets_on_repository_id" - add_index "changesets", ["user_id"], :name => "index_changesets_on_user_id" - - create_table "changesets_issues", :id => false, :force => true do |t| - t.integer "changeset_id", :null => false - t.integer "issue_id", :null => false - end - - add_index "changesets_issues", ["changeset_id", "issue_id"], :name => "changesets_issues_ids", :unique => true - - create_table "code_review_assignments", :force => true do |t| - t.integer "issue_id" - t.integer "change_id" - t.integer "attachment_id" - t.string "file_path" - t.string "rev" - t.string "rev_to" - t.string "action_type" - t.integer "changeset_id" - end - - create_table "code_review_project_settings", :force => true do |t| - t.integer "project_id" - t.integer "tracker_id" - t.datetime "created_at" - t.datetime "updated_at" - t.integer "updated_by" - t.boolean "hide_code_review_tab", :default => false - t.integer "auto_relation", :default => 1 - t.integer "assignment_tracker_id" - t.text "auto_assign" - t.integer "lock_version", :default => 0, :null => false - t.boolean "tracker_in_review_dialog", :default => false - end - - create_table "code_review_user_settings", :force => true do |t| - t.integer "user_id", :default => 0, :null => false - t.integer "mail_notification", :default => 0, :null => false - t.datetime "created_at" - t.datetime "updated_at" - end - - create_table "code_reviews", :force => true do |t| - t.integer "project_id" - t.integer "change_id" - t.datetime "created_at" - t.datetime "updated_at" - t.integer "line" - t.integer "updated_by_id" - t.integer "lock_version", :default => 0, :null => false - t.integer "status_changed_from" - t.integer "status_changed_to" - t.integer "issue_id" - t.string "action_type" - t.string "file_path" - t.string "rev" - t.string "rev_to" - t.integer "attachment_id" - t.integer "file_count", :default => 0, :null => false - t.boolean "diff_all" - end - - create_table "comments", :force => true do |t| - t.string "commented_type", :limit => 30, :default => "", :null => false - t.integer "commented_id", :default => 0, :null => false - t.integer "author_id", :default => 0, :null => false - t.text "comments" - t.datetime "created_on", :null => false - t.datetime "updated_on", :null => false - end - - add_index "comments", ["author_id"], :name => "index_comments_on_author_id" - add_index "comments", ["commented_id", "commented_type"], :name => "index_comments_on_commented_id_and_commented_type" - - create_table "contest_notifications", :force => true do |t| - t.text "title" - t.text "content" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "contesting_projects", :force => true do |t| - t.integer "project_id" - t.string "contest_id" - t.integer "user_id" - t.string "description" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "reward" - end - - create_table "contesting_softapplications", :force => true do |t| - t.integer "softapplication_id" - t.integer "contest_id" - t.integer "user_id" - t.string "description" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "reward" - end - - create_table "contestnotifications", :force => true do |t| - t.integer "contest_id" - t.string "title" - t.string "summary" - t.text "description" - t.integer "author_id" - t.integer "notificationcomments_count" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "contests", :force => true do |t| - t.string "name" - t.string "budget", :default => "" - t.integer "author_id" - t.date "deadline" - t.string "description" - t.integer "commit" - t.string "password" - t.datetime "created_on", :null => false - t.datetime "updated_on", :null => false - end - - create_table "course_activities", :force => true do |t| - t.integer "user_id" - t.integer "course_id" - t.integer "course_act_id" - t.string "course_act_type" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "course_attachments", :force => true do |t| - t.string "filename" - t.string "disk_filename" - t.integer "filesize" - t.string "content_type" - t.string "digest" - t.integer "downloads" - t.string "author_id" - t.string "integer" - t.string "description" - t.string "disk_directory" - t.integer "attachtype" - t.integer "is_public" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "container_id", :default => 0 - end - - create_table "course_groups", :force => true do |t| - t.string "name" - t.integer "course_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "course_infos", :force => true do |t| - t.integer "course_id" - t.integer "user_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "course_messages", :force => true do |t| - t.integer "user_id" - t.integer "course_id" - t.integer "course_message_id" - t.string "course_message_type" - t.integer "viewed" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "content" - t.integer "status" - end - - create_table "course_statuses", :force => true do |t| - t.integer "changesets_count" - t.integer "watchers_count" - t.integer "course_id" - t.float "grade", :default => 0.0 - t.integer "course_ac_para", :default => 0 - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "courses", :force => true do |t| - t.integer "tea_id" - t.string "name" - t.integer "state" - t.string "code" - t.integer "time" - t.string "extra" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "location" - t.string "term" - t.string "string" - t.string "password" - t.string "setup_time" - t.string "endup_time" - t.string "class_period" - t.integer "school_id" - t.text "description" - t.integer "status", :default => 1 - t.integer "attachmenttype", :default => 2 - t.integer "lft" - t.integer "rgt" - t.integer "is_public", :limit => 1, :default => 1 - t.integer "inherit_members", :limit => 1, :default => 1 - t.integer "open_student", :default => 0 - end - - create_table "custom_fields", :force => true do |t| - t.string "type", :limit => 30, :default => "", :null => false - t.string "name", :limit => 30, :default => "", :null => false - t.string "field_format", :limit => 30, :default => "", :null => false - t.text "possible_values" - t.string "regexp", :default => "" - t.integer "min_length", :default => 0, :null => false - t.integer "max_length", :default => 0, :null => false - t.boolean "is_required", :default => false, :null => false - t.boolean "is_for_all", :default => false, :null => false - t.boolean "is_filter", :default => false, :null => false - t.integer "position", :default => 1 - t.boolean "searchable", :default => false - t.text "default_value" - t.boolean "editable", :default => true - t.boolean "visible", :default => true, :null => false - t.boolean "multiple", :default => false - end - - add_index "custom_fields", ["id", "type"], :name => "index_custom_fields_on_id_and_type" - - create_table "custom_fields_projects", :id => false, :force => true do |t| - t.integer "custom_field_id", :default => 0, :null => false - t.integer "project_id", :default => 0, :null => false - end - - add_index "custom_fields_projects", ["custom_field_id", "project_id"], :name => "index_custom_fields_projects_on_custom_field_id_and_project_id", :unique => true - - create_table "custom_fields_trackers", :id => false, :force => true do |t| - t.integer "custom_field_id", :default => 0, :null => false - t.integer "tracker_id", :default => 0, :null => false - end - - add_index "custom_fields_trackers", ["custom_field_id", "tracker_id"], :name => "index_custom_fields_trackers_on_custom_field_id_and_tracker_id", :unique => true - - create_table "custom_values", :force => true do |t| - t.string "customized_type", :limit => 30, :default => "", :null => false - t.integer "customized_id", :default => 0, :null => false - t.integer "custom_field_id", :default => 0, :null => false - t.text "value" - end - - add_index "custom_values", ["custom_field_id"], :name => "index_custom_values_on_custom_field_id" - add_index "custom_values", ["customized_type", "customized_id"], :name => "custom_values_customized" - - create_table "delayed_jobs", :force => true do |t| - t.integer "priority", :default => 0, :null => false - t.integer "attempts", :default => 0, :null => false - t.text "handler", :null => false - t.text "last_error" - t.datetime "run_at" - t.datetime "locked_at" - t.datetime "failed_at" - t.string "locked_by" - t.string "queue" - t.datetime "created_at" - t.datetime "updated_at" - end - - add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority" - - create_table "discuss_demos", :force => true do |t| - t.string "title" - t.text "body" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "documents", :force => true do |t| - t.integer "project_id", :default => 0, :null => false - t.integer "category_id", :default => 0, :null => false - t.string "title", :limit => 60, :default => "", :null => false - t.text "description" - t.datetime "created_on" - t.integer "user_id", :default => 0 - t.integer "is_public", :default => 1 - end - - add_index "documents", ["category_id"], :name => "index_documents_on_category_id" - 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" - t.string "File" - t.string "IPLine" - t.string "Review" - t.string "Category" - t.string "Defect" - t.string "PreConditions" - t.string "StartLine" - t.integer "project_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "enabled_modules", :force => true do |t| - t.integer "project_id" - t.string "name", :null => false - t.integer "course_id" - end - - add_index "enabled_modules", ["project_id"], :name => "enabled_modules_project_id" - - create_table "enumerations", :force => true do |t| - t.string "name", :limit => 30, :default => "", :null => false - t.integer "position", :default => 1 - t.boolean "is_default", :default => false, :null => false - t.string "type" - t.boolean "active", :default => true, :null => false - t.integer "project_id" - t.integer "parent_id" - t.string "position_name", :limit => 30 - end - - add_index "enumerations", ["id", "type"], :name => "index_enumerations_on_id_and_type" - add_index "enumerations", ["project_id"], :name => "index_enumerations_on_project_id" - - create_table "first_pages", :force => true do |t| - t.string "web_title" - t.string "title" - t.text "description" - t.string "page_type" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "sort_type" - t.integer "image_width", :default => 107 - t.integer "image_height", :default => 63 - t.integer "show_course", :default => 1 - t.integer "show_contest", :default => 1 - end - - create_table "forge_activities", :force => true do |t| - t.integer "user_id" - t.integer "project_id" - t.integer "forge_act_id" - t.string "forge_act_type" - t.integer "org_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - add_index "forge_activities", ["forge_act_id"], :name => "index_forge_activities_on_forge_act_id" - - create_table "forge_messages", :force => true do |t| - t.integer "user_id" - t.integer "project_id" - t.integer "forge_message_id" - t.string "forge_message_type" - t.integer "viewed" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "secret_key" - t.integer "status" - end - - create_table "forums", :force => true do |t| - t.string "name", :null => false - t.text "description" - t.integer "topic_count", :default => 0 - t.integer "memo_count", :default => 0 - t.integer "last_memo_id", :default => 0 - t.integer "creator_id", :null => false - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "sticky" - t.integer "locked" - end - - create_table "groups_users", :id => false, :force => true do |t| - t.integer "group_id", :null => false - t.integer "user_id", :null => false - end - - add_index "groups_users", ["group_id", "user_id"], :name => "groups_users_ids", :unique => true - - create_table "homework_attaches", :force => true do |t| - t.integer "bid_id" - t.integer "user_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "reward" - t.string "name" - t.text "description" - t.integer "state" - t.integer "project_id", :default => 0 - t.float "score", :default => 0.0 - t.integer "is_teacher_score", :default => 0 - end - - add_index "homework_attaches", ["bid_id"], :name => "index_homework_attaches_on_bid_id" - - create_table "homework_commons", :force => true do |t| - t.string "name" - t.integer "user_id" - t.text "description" - t.date "publish_time" - t.date "end_time" - t.integer "homework_type", :default => 1 - t.string "late_penalty" - t.integer "course_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "teacher_priority", :default => 1 - end - - create_table "homework_detail_manuals", :force => true do |t| - t.float "ta_proportion" - t.integer "comment_status" - t.date "evaluation_start" - t.date "evaluation_end" - t.integer "evaluation_num" - t.integer "absence_penalty", :default => 1 - t.integer "homework_common_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "homework_detail_programings", :force => true do |t| - t.string "language" - t.text "standard_code", :limit => 2147483647 - t.integer "homework_common_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.float "ta_proportion", :default => 0.1 - t.integer "question_id" - end - - create_table "homework_evaluations", :force => true do |t| - t.string "user_id" - t.string "homework_attach_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "homework_for_courses", :force => true do |t| - t.integer "course_id" - t.integer "bid_id" - end - - add_index "homework_for_courses", ["bid_id"], :name => "index_homework_for_courses_on_bid_id" - add_index "homework_for_courses", ["course_id"], :name => "index_homework_for_courses_on_course_id" - - create_table "homework_tests", :force => true do |t| - t.text "input" - t.text "output" - t.integer "homework_common_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "result" - t.text "error_msg" - end - - create_table "homework_users", :force => true do |t| - t.string "homework_attach_id" - t.string "user_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "invite_lists", :force => true do |t| - t.integer "project_id" - t.integer "user_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "mail" - end - - create_table "issue_categories", :force => true do |t| - t.integer "project_id", :default => 0, :null => false - t.string "name", :limit => 30, :default => "", :null => false - t.integer "assigned_to_id" - end - - add_index "issue_categories", ["assigned_to_id"], :name => "index_issue_categories_on_assigned_to_id" - add_index "issue_categories", ["project_id"], :name => "issue_categories_project_id" - - create_table "issue_relations", :force => true do |t| - t.integer "issue_from_id", :null => false - t.integer "issue_to_id", :null => false - t.string "relation_type", :default => "", :null => false - t.integer "delay" - end - - add_index "issue_relations", ["issue_from_id", "issue_to_id"], :name => "index_issue_relations_on_issue_from_id_and_issue_to_id", :unique => true - add_index "issue_relations", ["issue_from_id"], :name => "index_issue_relations_on_issue_from_id" - add_index "issue_relations", ["issue_to_id"], :name => "index_issue_relations_on_issue_to_id" - - create_table "issue_statuses", :force => true do |t| - t.string "name", :limit => 30, :default => "", :null => false - t.boolean "is_closed", :default => false, :null => false - t.boolean "is_default", :default => false, :null => false - t.integer "position", :default => 1 - t.integer "default_done_ratio" - end - - add_index "issue_statuses", ["is_closed"], :name => "index_issue_statuses_on_is_closed" - add_index "issue_statuses", ["is_default"], :name => "index_issue_statuses_on_is_default" - add_index "issue_statuses", ["position"], :name => "index_issue_statuses_on_position" - - create_table "issues", :force => true do |t| - t.integer "tracker_id", :null => false - t.integer "project_id", :null => false - t.string "subject", :default => "", :null => false - t.text "description" - t.date "due_date" - t.integer "category_id" - t.integer "status_id", :null => false - t.integer "assigned_to_id" - t.integer "priority_id", :null => false - t.integer "fixed_version_id" - t.integer "author_id", :null => false - t.integer "lock_version", :default => 0, :null => false - t.datetime "created_on" - t.datetime "updated_on" - t.date "start_date" - t.integer "done_ratio", :default => 0, :null => false - t.float "estimated_hours" - t.integer "parent_id" - t.integer "root_id" - t.integer "lft" - t.integer "rgt" - t.boolean "is_private", :default => false, :null => false - t.datetime "closed_on" - t.integer "project_issues_index" - end - - add_index "issues", ["assigned_to_id"], :name => "index_issues_on_assigned_to_id" - add_index "issues", ["author_id"], :name => "index_issues_on_author_id" - add_index "issues", ["category_id"], :name => "index_issues_on_category_id" - add_index "issues", ["created_on"], :name => "index_issues_on_created_on" - add_index "issues", ["fixed_version_id"], :name => "index_issues_on_fixed_version_id" - add_index "issues", ["priority_id"], :name => "index_issues_on_priority_id" - add_index "issues", ["project_id"], :name => "issues_project_id" - add_index "issues", ["root_id", "lft", "rgt"], :name => "index_issues_on_root_id_and_lft_and_rgt" - add_index "issues", ["status_id"], :name => "index_issues_on_status_id" - add_index "issues", ["tracker_id"], :name => "index_issues_on_tracker_id" - - create_table "join_in_competitions", :force => true do |t| - t.integer "user_id" - t.integer "competition_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "join_in_contests", :force => true do |t| - t.integer "user_id" - t.integer "bid_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "journal_details", :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", ["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" - t.integer "reply_id" - end - - add_index "journal_replies", ["journal_id"], :name => "index_journal_replies_on_journal_id" - add_index "journal_replies", ["reply_id"], :name => "index_journal_replies_on_reply_id" - add_index "journal_replies", ["user_id"], :name => "index_journal_replies_on_user_id" - - create_table "journals", :force => true do |t| - t.integer "journalized_id", :default => 0, :null => false - t.string "journalized_type", :limit => 30, :default => "", :null => false - t.integer "user_id", :default => 0, :null => false - t.text "notes" - t.datetime "created_on", :null => false - t.boolean "private_notes", :default => false, :null => false - end - - add_index "journals", ["created_on"], :name => "index_journals_on_created_on" - add_index "journals", ["journalized_id", "journalized_type"], :name => "journals_journalized_id" - add_index "journals", ["journalized_id"], :name => "index_journals_on_journalized_id" - add_index "journals", ["user_id"], :name => "index_journals_on_user_id" - - create_table "journals_for_messages", :force => true do |t| - t.integer "jour_id" - t.string "jour_type" - t.integer "user_id" - t.text "notes" - t.integer "status" - t.integer "reply_id" - t.datetime "created_on", :null => false - t.datetime "updated_on", :null => false - t.string "m_parent_id" - t.boolean "is_readed" - t.integer "m_reply_count" - t.integer "m_reply_id" - t.integer "is_comprehensive_evaluation" - end - - create_table "kindeditor_assets", :force => true do |t| - t.string "asset" - t.integer "file_size" - t.string "file_type" - t.integer "owner_id" - t.string "asset_type" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "owner_type", :default => 0 - end - - create_table "member_roles", :force => true do |t| - t.integer "member_id", :null => false - t.integer "role_id", :null => false - t.integer "inherited_from" - end - - add_index "member_roles", ["member_id"], :name => "index_member_roles_on_member_id" - add_index "member_roles", ["role_id"], :name => "index_member_roles_on_role_id" - - create_table "members", :force => true do |t| - t.integer "user_id", :default => 0, :null => false - t.integer "project_id", :default => 0 - t.datetime "created_on" - t.boolean "mail_notification", :default => false, :null => false - t.integer "course_id", :default => -1 - t.integer "course_group_id", :default => 0 - end - - add_index "members", ["project_id"], :name => "index_members_on_project_id" - add_index "members", ["user_id", "project_id", "course_id"], :name => "index_members_on_user_id_and_project_id", :unique => true - add_index "members", ["user_id"], :name => "index_members_on_user_id" - - create_table "memo_messages", :force => true do |t| - t.integer "user_id" - t.integer "forum_id" - t.integer "memo_id" - t.string "memo_type" - t.integer "viewed" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "memos", :force => true do |t| - t.integer "forum_id", :null => false - t.integer "parent_id" - t.string "subject", :null => false - t.text "content", :null => false - t.integer "author_id", :null => false - t.integer "replies_count", :default => 0 - t.integer "last_reply_id" - t.boolean "lock", :default => false - t.boolean "sticky", :default => false - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "viewed_count", :default => 0 - end - - create_table "message_alls", :force => true do |t| - t.integer "user_id" - t.integer "message_id" - t.string "message_type" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "messages", :force => true do |t| - t.integer "board_id", :null => false - t.integer "parent_id" - t.string "subject", :default => "", :null => false - t.text "content" - t.integer "author_id" - t.integer "replies_count", :default => 0, :null => false - t.integer "last_reply_id" - t.datetime "created_on", :null => false - t.datetime "updated_on", :null => false - t.boolean "locked", :default => false - t.integer "sticky", :default => 0 - t.integer "reply_id" - end - - add_index "messages", ["author_id"], :name => "index_messages_on_author_id" - add_index "messages", ["board_id"], :name => "messages_board_id" - add_index "messages", ["created_on"], :name => "index_messages_on_created_on" - add_index "messages", ["last_reply_id"], :name => "index_messages_on_last_reply_id" - add_index "messages", ["parent_id"], :name => "messages_parent_id" - - create_table "news", :force => true do |t| - t.integer "project_id" - t.string "title", :limit => 60, :default => "", :null => false - t.string "summary", :default => "" - t.text "description" - t.integer "author_id", :default => 0, :null => false - t.datetime "created_on" - t.integer "comments_count", :default => 0, :null => false - t.integer "course_id" - end - - add_index "news", ["author_id"], :name => "index_news_on_author_id" - add_index "news", ["created_on"], :name => "index_news_on_created_on" - add_index "news", ["project_id"], :name => "news_project_id" - - create_table "no_uses", :force => true do |t| - t.integer "user_id", :null => false - t.string "no_use_type" - t.integer "no_use_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "notificationcomments", :force => true do |t| - t.string "notificationcommented_type" - t.integer "notificationcommented_id" - t.integer "author_id" - t.text "notificationcomments" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "onclick_times", :force => true do |t| - t.integer "user_id" - t.datetime "onclick_time" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "open_id_authentication_associations", :force => true do |t| - t.integer "issued" - t.integer "lifetime" - t.string "handle" - t.string "assoc_type" - t.binary "server_url" - t.binary "secret" - end - - create_table "open_id_authentication_nonces", :force => true do |t| - t.integer "timestamp", :null => false - t.string "server_url" - t.string "salt", :null => false - end - - create_table "open_source_projects", :force => true do |t| - t.string "name" - t.text "description" - t.integer "commit_count", :default => 0 - t.integer "code_line", :default => 0 - t.integer "users_count", :default => 0 - t.date "last_commit_time" - t.string "url" - t.date "date_collected" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "option_numbers", :force => true do |t| - t.integer "user_id" - t.integer "memo" - t.integer "messages_for_issues" - t.integer "issues_status" - t.integer "replay_for_message" - t.integer "replay_for_memo" - t.integer "follow" - t.integer "tread" - t.integer "praise_by_one" - t.integer "praise_by_two" - t.integer "praise_by_three" - t.integer "tread_by_one" - t.integer "tread_by_two" - t.integer "tread_by_three" - t.integer "changeset" - t.integer "document" - t.integer "attachment" - t.integer "issue_done_ratio" - t.integer "post_issue" - t.integer "score_type" - t.integer "total_score" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "project_id" - end - - create_table "organizations", :force => true do |t| - t.string "name" - t.string "logo_link" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "phone_app_versions", :force => true do |t| - t.string "version" - t.text "description" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "poll_answers", :force => true do |t| - t.integer "poll_question_id" - t.text "answer_text" - t.integer "answer_position" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "poll_questions", :force => true do |t| - t.string "question_title" - t.integer "question_type" - t.integer "is_necessary" - t.integer "poll_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "question_number" - end - - create_table "poll_users", :force => true do |t| - t.integer "user_id" - t.integer "poll_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "poll_votes", :force => true do |t| - t.integer "user_id" - t.integer "poll_question_id" - t.integer "poll_answer_id" - t.text "vote_text" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "polls", :force => true do |t| - t.string "polls_name" - t.string "polls_type" - t.integer "polls_group_id" - t.integer "polls_status" - t.integer "user_id" - t.datetime "published_at" - t.datetime "closed_at" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.text "polls_description" - t.integer "show_result", :default => 1 - end - - create_table "praise_tread_caches", :force => true do |t| - t.integer "object_id", :null => false - t.string "object_type" - t.integer "praise_num" - t.integer "tread_num" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "praise_treads", :force => true do |t| - t.integer "user_id", :null => false - t.integer "praise_tread_object_id" - t.string "praise_tread_object_type" - t.integer "praise_or_tread" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "principal_activities", :force => true do |t| - t.integer "user_id" - t.integer "principal_id" - t.integer "principal_act_id" - t.string "principal_act_type" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "project_infos", :force => true do |t| - t.integer "project_id" - t.integer "user_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "project_scores", :force => true do |t| - t.string "project_id" - t.integer "score" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "issue_num", :default => 0 - t.integer "issue_journal_num", :default => 0 - t.integer "news_num", :default => 0 - t.integer "documents_num", :default => 0 - t.integer "changeset_num", :default => 0 - t.integer "board_message_num", :default => 0 - end - - create_table "project_statuses", :force => true do |t| - t.integer "changesets_count" - t.integer "watchers_count" - t.integer "project_id" - t.integer "project_type" - t.float "grade", :default => 0.0 - t.integer "course_ac_para", :default => 0 - end - - add_index "project_statuses", ["grade"], :name => "index_project_statuses_on_grade" - - create_table "projecting_softapplictions", :force => true do |t| - t.integer "user_id" - t.integer "softapplication_id" - t.integer "project_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "projects", :force => true do |t| - t.string "name", :default => "", :null => false - t.text "description" - t.string "homepage", :default => "" - t.boolean "is_public", :default => true, :null => false - t.integer "parent_id" - t.datetime "created_on" - t.datetime "updated_on" - t.string "identifier" - t.integer "status", :default => 1, :null => false - t.integer "lft" - t.integer "rgt" - t.boolean "inherit_members", :default => false, :null => false - t.integer "project_type" - t.boolean "hidden_repo", :default => false, :null => false - t.integer "attachmenttype", :default => 1 - t.integer "user_id" - t.integer "dts_test", :default => 0 - t.string "enterprise_name" - t.integer "organization_id" - t.integer "project_new_type" - end - - add_index "projects", ["lft"], :name => "index_projects_on_lft" - add_index "projects", ["rgt"], :name => "index_projects_on_rgt" - - create_table "projects_trackers", :id => false, :force => true do |t| - t.integer "project_id", :default => 0, :null => false - t.integer "tracker_id", :default => 0, :null => false - end - - add_index "projects_trackers", ["project_id", "tracker_id"], :name => "projects_trackers_unique", :unique => true - add_index "projects_trackers", ["project_id"], :name => "projects_trackers_project_id" - - create_table "queries", :force => true do |t| - t.integer "project_id" - t.string "name", :default => "", :null => false - t.text "filters" - t.integer "user_id", :default => 0, :null => false - t.boolean "is_public", :default => false, :null => false - t.text "column_names" - t.text "sort_criteria" - t.string "group_by" - t.string "type" - end - - add_index "queries", ["project_id"], :name => "index_queries_on_project_id" - add_index "queries", ["user_id"], :name => "index_queries_on_user_id" - - create_table "relative_memo_to_open_source_projects", :force => true do |t| - t.integer "osp_id" - t.integer "relative_memo_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "relative_memos", :force => true do |t| - t.integer "osp_id" - t.integer "parent_id" - t.string "subject", :null => false - t.text "content", :limit => 16777215, :null => false - t.integer "author_id" - t.integer "replies_count", :default => 0 - t.integer "last_reply_id" - t.boolean "lock", :default => false - t.boolean "sticky", :default => false - t.boolean "is_quote", :default => false - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "viewed_count_crawl", :default => 0 - t.integer "viewed_count_local", :default => 0 - t.string "url" - t.string "username" - t.string "userhomeurl" - t.date "date_collected" - t.string "topic_resource" - end - - create_table "repositories", :force => true do |t| - t.integer "project_id", :default => 0, :null => false - t.string "url", :default => "", :null => false - t.string "login", :limit => 60, :default => "" - t.string "password", :default => "" - t.string "root_url", :default => "" - t.string "type" - t.string "path_encoding", :limit => 64 - t.string "log_encoding", :limit => 64 - t.text "extra_info" - t.string "identifier" - t.boolean "is_default", :default => false - t.boolean "hidden", :default => false - end - - add_index "repositories", ["project_id"], :name => "index_repositories_on_project_id" - - create_table "rich_rich_files", :force => true do |t| - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "rich_file_file_name" - t.string "rich_file_content_type" - t.integer "rich_file_file_size" - t.datetime "rich_file_updated_at" - t.string "owner_type" - t.integer "owner_id" - t.text "uri_cache" - t.string "simplified_type", :default => "file" - end - - create_table "roles", :force => true do |t| - t.string "name", :limit => 30, :default => "", :null => false - t.integer "position", :default => 1 - t.boolean "assignable", :default => true - t.integer "builtin", :default => 0, :null => false - t.text "permissions" - t.string "issues_visibility", :limit => 30, :default => "default", :null => false - end - - create_table "schools", :force => true do |t| - t.string "name" - t.string "province" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "logo_link" - t.string "pinyin" - end - - create_table "seems_rateable_cached_ratings", :force => true do |t| - t.integer "cacheable_id", :limit => 8 - t.string "cacheable_type" - t.float "avg", :null => false - t.integer "cnt", :null => false - t.string "dimension" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "seems_rateable_rates", :force => true do |t| - t.integer "rater_id", :limit => 8 - t.integer "rateable_id" - t.string "rateable_type" - t.float "stars", :null => false - t.string "dimension" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "is_teacher_score", :default => 0 - end - - create_table "settings", :force => true do |t| - t.string "name", :default => "", :null => false - t.text "value" - t.datetime "updated_on" - end - - add_index "settings", ["name"], :name => "index_settings_on_name" - - create_table "shares", :force => true do |t| - t.date "created_on" - t.string "url" - t.string "title" - t.integer "share_type" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "project_id" - t.integer "user_id" - t.string "description" - end - - create_table "softapplications", :force => true do |t| - t.string "name" - t.text "description" - t.integer "app_type_id" - t.string "app_type_name" - t.string "android_min_version_available" - t.integer "user_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "contest_id" - t.integer "softapplication_id" - t.integer "is_public" - t.string "application_developers" - t.string "deposit_project_url" - t.string "deposit_project" - t.integer "project_id" - end - - create_table "student_work_tests", :force => true do |t| - t.integer "student_work_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "status", :default => 9 - t.text "results" - t.text "src" - end - - create_table "student_works", :force => true do |t| - t.string "name" - t.text "description", :limit => 2147483647 - t.integer "homework_common_id" - t.integer "user_id" - t.float "final_score" - t.float "teacher_score" - t.float "student_score" - t.float "teaching_asistant_score" - t.integer "project_id", :default => 0 - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "late_penalty", :default => 0 - t.integer "absence_penalty", :default => 0 - t.float "system_score", :default => 0.0 - t.boolean "is_test", :default => false - end - - create_table "student_works_evaluation_distributions", :force => true do |t| - t.integer "student_work_id" - t.integer "user_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "student_works_scores", :force => true do |t| - t.integer "student_work_id" - t.integer "user_id" - t.integer "score" - t.text "comment" - t.integer "reviewer_role" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "students_for_courses", :force => true do |t| - t.integer "student_id" - t.integer "course_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - add_index "students_for_courses", ["course_id"], :name => "index_students_for_courses_on_course_id" - add_index "students_for_courses", ["student_id"], :name => "index_students_for_courses_on_student_id" - - create_table "system_messages", :force => true do |t| - t.integer "user_id" - t.string "content" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.text "description" - t.string "subject" - end - - create_table "taggings", :force => true do |t| - t.integer "tag_id" - t.integer "taggable_id" - t.string "taggable_type" - t.integer "tagger_id" - t.string "tagger_type" - t.string "context", :limit => 128 - t.datetime "created_at" - end - - add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id" - add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context" - add_index "taggings", ["taggable_type"], :name => "index_taggings_on_taggable_type" - - create_table "tags", :force => true do |t| - t.string "name" - end - - create_table "teachers", :force => true do |t| - t.string "tea_name" - t.string "location" - t.integer "couurse_time" - t.integer "course_code" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "extra" - end - - create_table "time_entries", :force => true do |t| - t.integer "project_id", :null => false - t.integer "user_id", :null => false - t.integer "issue_id" - t.float "hours", :null => false - t.string "comments" - t.integer "activity_id", :null => false - t.date "spent_on", :null => false - t.integer "tyear", :null => false - t.integer "tmonth", :null => false - t.integer "tweek", :null => false - t.datetime "created_on", :null => false - t.datetime "updated_on", :null => false - end - - add_index "time_entries", ["activity_id"], :name => "index_time_entries_on_activity_id" - add_index "time_entries", ["created_on"], :name => "index_time_entries_on_created_on" - add_index "time_entries", ["issue_id"], :name => "time_entries_issue_id" - add_index "time_entries", ["project_id"], :name => "time_entries_project_id" - add_index "time_entries", ["user_id"], :name => "index_time_entries_on_user_id" - - create_table "tokens", :force => true do |t| - t.integer "user_id", :default => 0, :null => false - t.string "action", :limit => 30, :default => "", :null => false - t.string "value", :limit => 40, :default => "", :null => false - t.datetime "created_on", :null => false - end - - add_index "tokens", ["user_id"], :name => "index_tokens_on_user_id" - add_index "tokens", ["value"], :name => "tokens_value", :unique => true - - create_table "trackers", :force => true do |t| - t.string "name", :limit => 30, :default => "", :null => false - t.boolean "is_in_chlog", :default => false, :null => false - t.integer "position", :default => 1 - t.boolean "is_in_roadmap", :default => true, :null => false - t.integer "fields_bits", :default => 0 - end - - create_table "user_activities", :force => true do |t| - t.string "act_type" - t.integer "act_id" - t.string "container_type" - t.integer "container_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "user_id" - end - - create_table "user_extensions", :force => true do |t| - t.integer "user_id", :null => false - t.date "birthday" - t.string "brief_introduction" - t.integer "gender" - t.string "location" - t.string "occupation" - t.integer "work_experience" - t.integer "zip_code" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "technical_title" - t.integer "identity" - t.string "student_id" - t.string "teacher_realname" - t.string "student_realname" - t.string "location_city" - t.integer "school_id" - t.string "description", :default => "" - end - - create_table "user_feedback_messages", :force => true do |t| - t.integer "user_id" - t.integer "journals_for_message_id" - t.string "journals_for_message_type" - t.integer "viewed" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "user_grades", :force => true do |t| - t.integer "user_id", :null => false - t.integer "project_id", :null => false - t.float "grade", :default => 0.0 - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - add_index "user_grades", ["grade"], :name => "index_user_grades_on_grade" - add_index "user_grades", ["project_id"], :name => "index_user_grades_on_project_id" - add_index "user_grades", ["user_id"], :name => "index_user_grades_on_user_id" - - create_table "user_levels", :force => true do |t| - t.integer "user_id" - t.integer "level" - end - - create_table "user_preferences", :force => true do |t| - t.integer "user_id", :default => 0, :null => false - t.text "others" - t.boolean "hide_mail", :default => false - t.string "time_zone" - end - - add_index "user_preferences", ["user_id"], :name => "index_user_preferences_on_user_id" - - create_table "user_score_details", :force => true do |t| - t.integer "current_user_id" - t.integer "target_user_id" - t.string "score_type" - t.string "score_action" - t.integer "user_id" - t.integer "old_score" - t.integer "new_score" - t.integer "current_user_level" - t.integer "target_user_level" - t.integer "score_changeable_obj_id" - t.string "score_changeable_obj_type" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "user_scores", :force => true do |t| - t.integer "user_id", :null => false - t.integer "collaboration" - t.integer "influence" - t.integer "skill" - t.integer "active" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "user_statuses", :force => true do |t| - t.integer "changesets_count" - t.integer "watchers_count" - t.integer "user_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.float "grade", :default => 0.0 - end - - add_index "user_statuses", ["changesets_count"], :name => "index_user_statuses_on_changesets_count" - add_index "user_statuses", ["grade"], :name => "index_user_statuses_on_grade" - add_index "user_statuses", ["watchers_count"], :name => "index_user_statuses_on_watchers_count" - - create_table "users", :force => true do |t| - t.string "login", :default => "", :null => false - t.string "hashed_password", :limit => 40, :default => "", :null => false - t.string "firstname", :limit => 30, :default => "", :null => false - t.string "lastname", :default => "", :null => false - t.string "mail", :limit => 60, :default => "", :null => false - t.boolean "admin", :default => false, :null => false - t.integer "status", :default => 1, :null => false - t.datetime "last_login_on" - t.string "language", :limit => 5, :default => "" - t.integer "auth_source_id" - t.datetime "created_on" - t.datetime "updated_on" - t.string "type" - t.string "identity_url" - t.string "mail_notification", :default => "", :null => false - t.string "salt", :limit => 64 - end - - add_index "users", ["auth_source_id"], :name => "index_users_on_auth_source_id" - add_index "users", ["id", "type"], :name => "index_users_on_id_and_type" - add_index "users", ["type"], :name => "index_users_on_type" - - create_table "versions", :force => true do |t| - t.integer "project_id", :default => 0, :null => false - t.string "name", :default => "", :null => false - t.string "description", :default => "" - t.date "effective_date" - t.datetime "created_on" - t.datetime "updated_on" - t.string "wiki_page_title" - t.string "status", :default => "open" - t.string "sharing", :default => "none", :null => false - end - - add_index "versions", ["project_id"], :name => "versions_project_id" - add_index "versions", ["sharing"], :name => "index_versions_on_sharing" - - create_table "visitors", :force => true do |t| - t.integer "user_id" - t.integer "master_id" - t.datetime "updated_on" - t.datetime "created_on" - end - - add_index "visitors", ["master_id"], :name => "index_visitors_master_id" - add_index "visitors", ["updated_on"], :name => "index_visitors_updated_on" - add_index "visitors", ["user_id"], :name => "index_visitors_user_id" - - create_table "watchers", :force => true do |t| - t.string "watchable_type", :default => "", :null => false - t.integer "watchable_id", :default => 0, :null => false - t.integer "user_id" - end - - add_index "watchers", ["user_id", "watchable_type"], :name => "watchers_user_id_type" - add_index "watchers", ["user_id"], :name => "index_watchers_on_user_id" - add_index "watchers", ["watchable_id", "watchable_type"], :name => "index_watchers_on_watchable_id_and_watchable_type" - - create_table "web_footer_companies", :force => true do |t| - t.string "name" - t.string "logo_size" - t.string "url" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "web_footer_oranizers", :force => true do |t| - t.string "name" - t.text "description" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "wiki_content_versions", :force => true do |t| - t.integer "wiki_content_id", :null => false - t.integer "page_id", :null => false - t.integer "author_id" - t.binary "data", :limit => 2147483647 - t.string "compression", :limit => 6, :default => "" - t.string "comments", :default => "" - t.datetime "updated_on", :null => false - t.integer "version", :null => false - end - - add_index "wiki_content_versions", ["updated_on"], :name => "index_wiki_content_versions_on_updated_on" - add_index "wiki_content_versions", ["wiki_content_id"], :name => "wiki_content_versions_wcid" - - create_table "wiki_contents", :force => true do |t| - t.integer "page_id", :null => false - t.integer "author_id" - t.text "text", :limit => 2147483647 - t.string "comments", :default => "" - t.datetime "updated_on", :null => false - t.integer "version", :null => false - end - - add_index "wiki_contents", ["author_id"], :name => "index_wiki_contents_on_author_id" - add_index "wiki_contents", ["page_id"], :name => "wiki_contents_page_id" - - create_table "wiki_pages", :force => true do |t| - t.integer "wiki_id", :null => false - t.string "title", :null => false - t.datetime "created_on", :null => false - t.boolean "protected", :default => false, :null => false - t.integer "parent_id" - end - - add_index "wiki_pages", ["parent_id"], :name => "index_wiki_pages_on_parent_id" - add_index "wiki_pages", ["wiki_id", "title"], :name => "wiki_pages_wiki_id_title" - add_index "wiki_pages", ["wiki_id"], :name => "index_wiki_pages_on_wiki_id" - - create_table "wiki_redirects", :force => true do |t| - t.integer "wiki_id", :null => false - t.string "title" - t.string "redirects_to" - t.datetime "created_on", :null => false - end - - add_index "wiki_redirects", ["wiki_id", "title"], :name => "wiki_redirects_wiki_id_title" - add_index "wiki_redirects", ["wiki_id"], :name => "index_wiki_redirects_on_wiki_id" - - create_table "wikis", :force => true do |t| - t.integer "project_id", :null => false - t.string "start_page", :null => false - t.integer "status", :default => 1, :null => false - end - - add_index "wikis", ["project_id"], :name => "wikis_project_id" - - create_table "workflows", :force => true do |t| - t.integer "tracker_id", :default => 0, :null => false - t.integer "old_status_id", :default => 0, :null => false - t.integer "new_status_id", :default => 0, :null => false - t.integer "role_id", :default => 0, :null => false - t.boolean "assignee", :default => false, :null => false - t.boolean "author", :default => false, :null => false - t.string "type", :limit => 30 - t.string "field_name", :limit => 30 - t.string "rule", :limit => 30 - end - - add_index "workflows", ["new_status_id"], :name => "index_workflows_on_new_status_id" - add_index "workflows", ["old_status_id"], :name => "index_workflows_on_old_status_id" - add_index "workflows", ["role_id", "tracker_id", "old_status_id"], :name => "wkfs_role_tracker_old_status" - add_index "workflows", ["role_id"], :name => "index_workflows_on_role_id" - - create_table "works_categories", :force => true do |t| - t.string "category" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "zip_packs", :force => true do |t| - t.integer "user_id" - t.integer "homework_id" - t.string "file_digest" - t.string "file_path" - t.integer "pack_times", :default => 1 - t.integer "pack_size", :default => 0 - t.text "file_digests" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - -end +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended to check this file into your version control system. + +ActiveRecord::Schema.define(:version => 20151022071804) do + + create_table "activities", :force => true do |t| + t.integer "act_id", :null => false + t.string "act_type", :null => false + t.integer "user_id", :null => false + t.integer "activity_container_id" + t.string "activity_container_type", :default => "" + t.datetime "created_at" + end + + add_index "activities", ["act_id", "act_type"], :name => "index_activities_on_act_id_and_act_type" + add_index "activities", ["user_id", "act_type"], :name => "index_activities_on_user_id_and_act_type" + add_index "activities", ["user_id"], :name => "index_activities_on_user_id" + + create_table "activity_notifies", :force => true do |t| + t.integer "activity_container_id" + t.string "activity_container_type" + t.integer "activity_id" + t.string "activity_type" + t.integer "notify_to" + t.datetime "created_on" + t.integer "is_read" + end + + add_index "activity_notifies", ["activity_container_id", "activity_container_type"], :name => "index_an_activity_container_id" + add_index "activity_notifies", ["created_on"], :name => "index_an_created_on" + add_index "activity_notifies", ["notify_to"], :name => "index_an_notify_to" + + create_table "api_keys", :force => true do |t| + t.string "access_token" + t.datetime "expires_at" + t.integer "user_id" + t.boolean "active", :default => true + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "api_keys", ["access_token"], :name => "index_api_keys_on_access_token" + add_index "api_keys", ["user_id"], :name => "index_api_keys_on_user_id" + + create_table "applied_projects", :force => true do |t| + t.integer "project_id", :null => false + t.integer "user_id", :null => false + end + + create_table "apply_project_masters", :force => true do |t| + t.integer "user_id" + t.string "apply_type" + t.integer "apply_id" + t.integer "status" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "attachments", :force => true do |t| + t.integer "container_id" + t.string "container_type", :limit => 30 + t.string "filename", :default => "", :null => false + t.string "disk_filename", :default => "", :null => false + t.integer "filesize", :default => 0, :null => false + t.string "content_type", :default => "" + t.string "digest", :limit => 40, :default => "", :null => false + t.integer "downloads", :default => 0, :null => false + t.integer "author_id", :default => 0, :null => false + t.datetime "created_on" + t.string "description" + t.string "disk_directory" + t.integer "attachtype", :default => 1 + t.integer "is_public", :default => 1 + t.integer "copy_from" + t.integer "quotes" + end + + add_index "attachments", ["author_id"], :name => "index_attachments_on_author_id" + add_index "attachments", ["container_id", "container_type"], :name => "index_attachments_on_container_id_and_container_type" + add_index "attachments", ["created_on"], :name => "index_attachments_on_created_on" + + create_table "attachmentstypes", :force => true do |t| + t.integer "typeId", :null => false + t.string "typeName", :limit => 50 + end + + create_table "auth_sources", :force => true do |t| + t.string "type", :limit => 30, :default => "", :null => false + t.string "name", :limit => 60, :default => "", :null => false + t.string "host", :limit => 60 + t.integer "port" + t.string "account" + t.string "account_password", :default => "" + t.string "base_dn" + t.string "attr_login", :limit => 30 + t.string "attr_firstname", :limit => 30 + t.string "attr_lastname", :limit => 30 + t.string "attr_mail", :limit => 30 + t.boolean "onthefly_register", :default => false, :null => false + t.boolean "tls", :default => false, :null => false + t.string "filter" + t.integer "timeout" + end + + add_index "auth_sources", ["id", "type"], :name => "index_auth_sources_on_id_and_type" + + create_table "biding_projects", :force => true do |t| + t.integer "project_id" + t.integer "bid_id" + t.integer "user_id" + t.string "description" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "reward" + end + + create_table "bids", :force => true do |t| + t.string "name" + t.string "budget", :null => false + t.integer "author_id" + t.date "deadline" + t.text "description" + t.datetime "created_on", :null => false + t.datetime "updated_on", :null => false + t.integer "commit" + t.integer "reward_type" + t.integer "homework_type" + t.integer "parent_id" + t.string "password" + t.integer "is_evaluation" + t.integer "proportion", :default => 60 + t.integer "comment_status", :default => 0 + t.integer "evaluation_num", :default => 3 + t.integer "open_anonymous_evaluation", :default => 1 + end + + create_table "blog_comments", :force => true do |t| + t.integer "blog_id", :null => false + t.integer "parent_id" + t.string "title", :default => "", :null => false + t.text "content" + t.integer "author_id" + t.integer "comments_count", :default => 0, :null => false + t.integer "last_comment_id" + t.datetime "created_on", :null => false + t.datetime "updated_on", :null => false + t.boolean "locked", :default => false + t.integer "sticky", :default => 0 + t.integer "reply_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "blogs", :force => true do |t| + t.string "name", :default => "", :null => false + t.text "description" + t.integer "position", :default => 1 + t.integer "article_count", :default => 0, :null => false + t.integer "comments_count", :default => 0, :null => false + t.integer "last_comments_id" + t.integer "parent_id" + t.integer "author_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "boards", :force => true do |t| + t.integer "project_id", :null => false + t.string "name", :default => "", :null => false + t.string "description" + t.integer "position", :default => 1 + t.integer "topics_count", :default => 0, :null => false + t.integer "messages_count", :default => 0, :null => false + t.integer "last_message_id" + t.integer "parent_id" + t.integer "course_id" + end + + add_index "boards", ["last_message_id"], :name => "index_boards_on_last_message_id" + add_index "boards", ["project_id"], :name => "boards_project_id" + + create_table "bug_to_osps", :force => true do |t| + t.integer "osp_id" + t.integer "relative_memo_id" + t.string "description" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "changes", :force => true do |t| + t.integer "changeset_id", :null => false + t.string "action", :limit => 1, :default => "", :null => false + t.text "path", :null => false + t.text "from_path" + t.string "from_revision" + t.string "revision" + t.string "branch" + end + + add_index "changes", ["changeset_id"], :name => "changesets_changeset_id" + + create_table "changeset_parents", :id => false, :force => true do |t| + t.integer "changeset_id", :null => false + t.integer "parent_id", :null => false + end + + add_index "changeset_parents", ["changeset_id"], :name => "changeset_parents_changeset_ids" + add_index "changeset_parents", ["parent_id"], :name => "changeset_parents_parent_ids" + + create_table "changesets", :force => true do |t| + t.integer "repository_id", :null => false + t.string "revision", :null => false + t.string "committer" + t.datetime "committed_on", :null => false + t.text "comments" + t.date "commit_date" + t.string "scmid" + t.integer "user_id" + end + + add_index "changesets", ["committed_on"], :name => "index_changesets_on_committed_on" + add_index "changesets", ["repository_id", "revision"], :name => "changesets_repos_rev", :unique => true + add_index "changesets", ["repository_id", "scmid"], :name => "changesets_repos_scmid" + add_index "changesets", ["repository_id"], :name => "index_changesets_on_repository_id" + add_index "changesets", ["user_id"], :name => "index_changesets_on_user_id" + + create_table "changesets_issues", :id => false, :force => true do |t| + t.integer "changeset_id", :null => false + t.integer "issue_id", :null => false + end + + add_index "changesets_issues", ["changeset_id", "issue_id"], :name => "changesets_issues_ids", :unique => true + + create_table "code_review_assignments", :force => true do |t| + t.integer "issue_id" + t.integer "change_id" + t.integer "attachment_id" + t.string "file_path" + t.string "rev" + t.string "rev_to" + t.string "action_type" + t.integer "changeset_id" + end + + create_table "code_review_project_settings", :force => true do |t| + t.integer "project_id" + t.integer "tracker_id" + t.datetime "created_at" + t.datetime "updated_at" + t.integer "updated_by" + t.boolean "hide_code_review_tab", :default => false + t.integer "auto_relation", :default => 1 + t.integer "assignment_tracker_id" + t.text "auto_assign" + t.integer "lock_version", :default => 0, :null => false + t.boolean "tracker_in_review_dialog", :default => false + end + + create_table "code_review_user_settings", :force => true do |t| + t.integer "user_id", :default => 0, :null => false + t.integer "mail_notification", :default => 0, :null => false + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "code_reviews", :force => true do |t| + t.integer "project_id" + t.integer "change_id" + t.datetime "created_at" + t.datetime "updated_at" + t.integer "line" + t.integer "updated_by_id" + t.integer "lock_version", :default => 0, :null => false + t.integer "status_changed_from" + t.integer "status_changed_to" + t.integer "issue_id" + t.string "action_type" + t.string "file_path" + t.string "rev" + t.string "rev_to" + t.integer "attachment_id" + t.integer "file_count", :default => 0, :null => false + t.boolean "diff_all" + end + + create_table "comments", :force => true do |t| + t.string "commented_type", :limit => 30, :default => "", :null => false + t.integer "commented_id", :default => 0, :null => false + t.integer "author_id", :default => 0, :null => false + t.text "comments" + t.datetime "created_on", :null => false + t.datetime "updated_on", :null => false + end + + add_index "comments", ["author_id"], :name => "index_comments_on_author_id" + add_index "comments", ["commented_id", "commented_type"], :name => "index_comments_on_commented_id_and_commented_type" + + create_table "contest_notifications", :force => true do |t| + t.text "title" + t.text "content" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "contesting_projects", :force => true do |t| + t.integer "project_id" + t.string "contest_id" + t.integer "user_id" + t.string "description" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "reward" + end + + create_table "contesting_softapplications", :force => true do |t| + t.integer "softapplication_id" + t.integer "contest_id" + t.integer "user_id" + t.string "description" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "reward" + end + + create_table "contestnotifications", :force => true do |t| + t.integer "contest_id" + t.string "title" + t.string "summary" + t.text "description" + t.integer "author_id" + t.integer "notificationcomments_count" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "contests", :force => true do |t| + t.string "name" + t.string "budget", :default => "" + t.integer "author_id" + t.date "deadline" + t.string "description" + t.integer "commit" + t.string "password" + t.datetime "created_on", :null => false + t.datetime "updated_on", :null => false + end + + create_table "course_activities", :force => true do |t| + t.integer "user_id" + t.integer "course_id" + t.integer "course_act_id" + t.string "course_act_type" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "course_attachments", :force => true do |t| + t.string "filename" + t.string "disk_filename" + t.integer "filesize" + t.string "content_type" + t.string "digest" + t.integer "downloads" + t.string "author_id" + t.string "integer" + t.string "description" + t.string "disk_directory" + t.integer "attachtype" + t.integer "is_public" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "container_id", :default => 0 + end + + create_table "course_groups", :force => true do |t| + t.string "name" + t.integer "course_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "course_infos", :force => true do |t| + t.integer "course_id" + t.integer "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "course_messages", :force => true do |t| + t.integer "user_id" + t.integer "course_id" + t.integer "course_message_id" + t.string "course_message_type" + t.integer "viewed" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "content" + t.integer "status" + end + + create_table "course_statuses", :force => true do |t| + t.integer "changesets_count" + t.integer "watchers_count" + t.integer "course_id" + t.float "grade", :default => 0.0 + t.integer "course_ac_para", :default => 0 + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "courses", :force => true do |t| + t.integer "tea_id" + t.string "name" + t.integer "state" + t.string "code" + t.integer "time" + t.string "extra" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "location" + t.string "term" + t.string "string" + t.string "password" + t.string "setup_time" + t.string "endup_time" + t.string "class_period" + t.integer "school_id" + t.text "description" + t.integer "status", :default => 1 + t.integer "attachmenttype", :default => 2 + t.integer "lft" + t.integer "rgt" + t.integer "is_public", :limit => 1, :default => 1 + t.integer "inherit_members", :limit => 1, :default => 1 + t.integer "open_student", :default => 0 + end + + create_table "custom_fields", :force => true do |t| + t.string "type", :limit => 30, :default => "", :null => false + t.string "name", :limit => 30, :default => "", :null => false + t.string "field_format", :limit => 30, :default => "", :null => false + t.text "possible_values" + t.string "regexp", :default => "" + t.integer "min_length", :default => 0, :null => false + t.integer "max_length", :default => 0, :null => false + t.boolean "is_required", :default => false, :null => false + t.boolean "is_for_all", :default => false, :null => false + t.boolean "is_filter", :default => false, :null => false + t.integer "position", :default => 1 + t.boolean "searchable", :default => false + t.text "default_value" + t.boolean "editable", :default => true + t.boolean "visible", :default => true, :null => false + t.boolean "multiple", :default => false + end + + add_index "custom_fields", ["id", "type"], :name => "index_custom_fields_on_id_and_type" + + create_table "custom_fields_projects", :id => false, :force => true do |t| + t.integer "custom_field_id", :default => 0, :null => false + t.integer "project_id", :default => 0, :null => false + end + + add_index "custom_fields_projects", ["custom_field_id", "project_id"], :name => "index_custom_fields_projects_on_custom_field_id_and_project_id", :unique => true + + create_table "custom_fields_trackers", :id => false, :force => true do |t| + t.integer "custom_field_id", :default => 0, :null => false + t.integer "tracker_id", :default => 0, :null => false + end + + add_index "custom_fields_trackers", ["custom_field_id", "tracker_id"], :name => "index_custom_fields_trackers_on_custom_field_id_and_tracker_id", :unique => true + + create_table "custom_values", :force => true do |t| + t.string "customized_type", :limit => 30, :default => "", :null => false + t.integer "customized_id", :default => 0, :null => false + t.integer "custom_field_id", :default => 0, :null => false + t.text "value" + end + + add_index "custom_values", ["custom_field_id"], :name => "index_custom_values_on_custom_field_id" + add_index "custom_values", ["customized_type", "customized_id"], :name => "custom_values_customized" + + create_table "delayed_jobs", :force => true do |t| + t.integer "priority", :default => 0, :null => false + t.integer "attempts", :default => 0, :null => false + t.text "handler", :null => false + t.text "last_error" + t.datetime "run_at" + t.datetime "locked_at" + t.datetime "failed_at" + t.string "locked_by" + t.string "queue" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority" + + create_table "discuss_demos", :force => true do |t| + t.string "title" + t.text "body" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "documents", :force => true do |t| + t.integer "project_id", :default => 0, :null => false + t.integer "category_id", :default => 0, :null => false + t.string "title", :limit => 60, :default => "", :null => false + t.text "description" + t.datetime "created_on" + t.integer "user_id", :default => 0 + t.integer "is_public", :default => 1 + end + + add_index "documents", ["category_id"], :name => "index_documents_on_category_id" + add_index "documents", ["created_on"], :name => "index_documents_on_created_on" + add_index "documents", ["project_id"], :name => "documents_project_id" + + 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 "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" + t.datetime "updated_at" + t.integer "id", :null => false + end + + create_table "enabled_modules", :force => true do |t| + t.integer "project_id" + t.string "name", :null => false + t.integer "course_id" + end + + add_index "enabled_modules", ["project_id"], :name => "enabled_modules_project_id" + + create_table "enumerations", :force => true do |t| + t.string "name", :limit => 30, :default => "", :null => false + t.integer "position", :default => 1 + t.boolean "is_default", :default => false, :null => false + t.string "type" + t.boolean "active", :default => true, :null => false + t.integer "project_id" + t.integer "parent_id" + t.string "position_name", :limit => 30 + end + + add_index "enumerations", ["id", "type"], :name => "index_enumerations_on_id_and_type" + add_index "enumerations", ["project_id"], :name => "index_enumerations_on_project_id" + + create_table "first_pages", :force => true do |t| + t.string "web_title" + t.string "title" + t.text "description" + t.string "page_type" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "sort_type" + t.integer "image_width", :default => 107 + t.integer "image_height", :default => 63 + t.integer "show_course", :default => 1 + t.integer "show_contest", :default => 1 + end + + create_table "forge_activities", :force => true do |t| + t.integer "user_id" + t.integer "project_id" + t.integer "forge_act_id" + t.string "forge_act_type" + t.integer "org_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "forge_activities", ["forge_act_id"], :name => "index_forge_activities_on_forge_act_id" + + create_table "forge_messages", :force => true do |t| + t.integer "user_id" + t.integer "project_id" + t.integer "forge_message_id" + t.string "forge_message_type" + t.integer "viewed" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "secret_key" + t.integer "status" + end + + create_table "forums", :force => true do |t| + t.string "name", :null => false + t.text "description" + t.integer "topic_count", :default => 0 + t.integer "memo_count", :default => 0 + t.integer "last_memo_id", :default => 0 + t.integer "creator_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "sticky" + t.integer "locked" + end + + create_table "groups_users", :id => false, :force => true do |t| + t.integer "group_id", :null => false + t.integer "user_id", :null => false + end + + add_index "groups_users", ["group_id", "user_id"], :name => "groups_users_ids", :unique => true + + create_table "homework_attaches", :force => true do |t| + t.integer "bid_id" + t.integer "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "reward" + t.string "name" + t.text "description" + t.integer "state" + t.integer "project_id", :default => 0 + t.float "score", :default => 0.0 + t.integer "is_teacher_score", :default => 0 + end + + add_index "homework_attaches", ["bid_id"], :name => "index_homework_attaches_on_bid_id" + + create_table "homework_commons", :force => true do |t| + t.string "name" + t.integer "user_id" + t.text "description" + t.date "publish_time" + t.date "end_time" + t.integer "homework_type", :default => 1 + t.string "late_penalty" + t.integer "course_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "teacher_priority", :default => 1 + end + + create_table "homework_detail_manuals", :force => true do |t| + t.float "ta_proportion" + t.integer "comment_status" + t.date "evaluation_start" + t.date "evaluation_end" + t.integer "evaluation_num" + t.integer "absence_penalty", :default => 1 + t.integer "homework_common_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "homework_detail_programings", :force => true do |t| + t.string "language" + t.text "standard_code", :limit => 2147483647 + t.integer "homework_common_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.float "ta_proportion", :default => 0.1 + t.integer "question_id" + end + + create_table "homework_evaluations", :force => true do |t| + t.string "user_id" + t.string "homework_attach_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "homework_for_courses", :force => true do |t| + t.integer "course_id" + t.integer "bid_id" + end + + add_index "homework_for_courses", ["bid_id"], :name => "index_homework_for_courses_on_bid_id" + add_index "homework_for_courses", ["course_id"], :name => "index_homework_for_courses_on_course_id" + + create_table "homework_tests", :force => true do |t| + t.text "input" + t.text "output" + t.integer "homework_common_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "result" + t.text "error_msg" + end + + create_table "homework_users", :force => true do |t| + t.string "homework_attach_id" + t.string "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "invite_lists", :force => true do |t| + t.integer "project_id" + t.integer "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "mail" + end + + create_table "issue_categories", :force => true do |t| + t.integer "project_id", :default => 0, :null => false + t.string "name", :limit => 30, :default => "", :null => false + t.integer "assigned_to_id" + end + + add_index "issue_categories", ["assigned_to_id"], :name => "index_issue_categories_on_assigned_to_id" + add_index "issue_categories", ["project_id"], :name => "issue_categories_project_id" + + create_table "issue_relations", :force => true do |t| + t.integer "issue_from_id", :null => false + t.integer "issue_to_id", :null => false + t.string "relation_type", :default => "", :null => false + t.integer "delay" + end + + add_index "issue_relations", ["issue_from_id", "issue_to_id"], :name => "index_issue_relations_on_issue_from_id_and_issue_to_id", :unique => true + add_index "issue_relations", ["issue_from_id"], :name => "index_issue_relations_on_issue_from_id" + add_index "issue_relations", ["issue_to_id"], :name => "index_issue_relations_on_issue_to_id" + + create_table "issue_statuses", :force => true do |t| + t.string "name", :limit => 30, :default => "", :null => false + t.boolean "is_closed", :default => false, :null => false + t.boolean "is_default", :default => false, :null => false + t.integer "position", :default => 1 + t.integer "default_done_ratio" + end + + add_index "issue_statuses", ["is_closed"], :name => "index_issue_statuses_on_is_closed" + add_index "issue_statuses", ["is_default"], :name => "index_issue_statuses_on_is_default" + add_index "issue_statuses", ["position"], :name => "index_issue_statuses_on_position" + + create_table "issues", :force => true do |t| + t.integer "tracker_id", :null => false + t.integer "project_id", :null => false + t.string "subject", :default => "", :null => false + t.text "description" + t.date "due_date" + t.integer "category_id" + t.integer "status_id", :null => false + t.integer "assigned_to_id" + t.integer "priority_id", :null => false + t.integer "fixed_version_id" + t.integer "author_id", :null => false + t.integer "lock_version", :default => 0, :null => false + t.datetime "created_on" + t.datetime "updated_on" + t.date "start_date" + t.integer "done_ratio", :default => 0, :null => false + t.float "estimated_hours" + t.integer "parent_id" + t.integer "root_id" + t.integer "lft" + t.integer "rgt" + t.boolean "is_private", :default => false, :null => false + t.datetime "closed_on" + t.integer "project_issues_index" + end + + add_index "issues", ["assigned_to_id"], :name => "index_issues_on_assigned_to_id" + add_index "issues", ["author_id"], :name => "index_issues_on_author_id" + add_index "issues", ["category_id"], :name => "index_issues_on_category_id" + add_index "issues", ["created_on"], :name => "index_issues_on_created_on" + add_index "issues", ["fixed_version_id"], :name => "index_issues_on_fixed_version_id" + add_index "issues", ["priority_id"], :name => "index_issues_on_priority_id" + add_index "issues", ["project_id"], :name => "issues_project_id" + add_index "issues", ["root_id", "lft", "rgt"], :name => "index_issues_on_root_id_and_lft_and_rgt" + add_index "issues", ["status_id"], :name => "index_issues_on_status_id" + add_index "issues", ["tracker_id"], :name => "index_issues_on_tracker_id" + + create_table "join_in_competitions", :force => true do |t| + t.integer "user_id" + t.integer "competition_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "join_in_contests", :force => true do |t| + t.integer "user_id" + t.integer "bid_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "journal_details", :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", ["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" + t.integer "reply_id" + end + + add_index "journal_replies", ["journal_id"], :name => "index_journal_replies_on_journal_id" + add_index "journal_replies", ["reply_id"], :name => "index_journal_replies_on_reply_id" + add_index "journal_replies", ["user_id"], :name => "index_journal_replies_on_user_id" + + create_table "journals", :force => true do |t| + t.integer "journalized_id", :default => 0, :null => false + t.string "journalized_type", :limit => 30, :default => "", :null => false + t.integer "user_id", :default => 0, :null => false + t.text "notes" + t.datetime "created_on", :null => false + t.boolean "private_notes", :default => false, :null => false + end + + add_index "journals", ["created_on"], :name => "index_journals_on_created_on" + add_index "journals", ["journalized_id", "journalized_type"], :name => "journals_journalized_id" + add_index "journals", ["journalized_id"], :name => "index_journals_on_journalized_id" + add_index "journals", ["user_id"], :name => "index_journals_on_user_id" + + create_table "journals_for_messages", :force => true do |t| + t.integer "jour_id" + t.string "jour_type" + t.integer "user_id" + t.text "notes" + t.integer "status" + t.integer "reply_id" + t.datetime "created_on", :null => false + t.datetime "updated_on", :null => false + t.string "m_parent_id" + t.boolean "is_readed" + t.integer "m_reply_count" + t.integer "m_reply_id" + t.integer "is_comprehensive_evaluation" + end + + create_table "kindeditor_assets", :force => true do |t| + t.string "asset" + t.integer "file_size" + t.string "file_type" + t.integer "owner_id" + t.string "asset_type" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "owner_type", :default => 0 + end + + create_table "member_roles", :force => true do |t| + t.integer "member_id", :null => false + t.integer "role_id", :null => false + t.integer "inherited_from" + end + + add_index "member_roles", ["member_id"], :name => "index_member_roles_on_member_id" + add_index "member_roles", ["role_id"], :name => "index_member_roles_on_role_id" + + create_table "members", :force => true do |t| + t.integer "user_id", :default => 0, :null => false + t.integer "project_id", :default => 0 + t.datetime "created_on" + t.boolean "mail_notification", :default => false, :null => false + t.integer "course_id", :default => -1 + t.integer "course_group_id", :default => 0 + end + + add_index "members", ["project_id"], :name => "index_members_on_project_id" + add_index "members", ["user_id", "project_id", "course_id"], :name => "index_members_on_user_id_and_project_id", :unique => true + add_index "members", ["user_id"], :name => "index_members_on_user_id" + + create_table "memo_messages", :force => true do |t| + t.integer "user_id" + t.integer "forum_id" + t.integer "memo_id" + t.string "memo_type" + t.integer "viewed" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "memos", :force => true do |t| + t.integer "forum_id", :null => false + t.integer "parent_id" + t.string "subject", :null => false + t.text "content", :null => false + t.integer "author_id", :null => false + t.integer "replies_count", :default => 0 + t.integer "last_reply_id" + t.boolean "lock", :default => false + t.boolean "sticky", :default => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "viewed_count", :default => 0 + end + + create_table "message_alls", :force => true do |t| + t.integer "user_id" + t.integer "message_id" + t.string "message_type" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "messages", :force => true do |t| + t.integer "board_id", :null => false + t.integer "parent_id" + t.string "subject", :default => "", :null => false + t.text "content" + t.integer "author_id" + t.integer "replies_count", :default => 0, :null => false + t.integer "last_reply_id" + t.datetime "created_on", :null => false + t.datetime "updated_on", :null => false + t.boolean "locked", :default => false + t.integer "sticky", :default => 0 + t.integer "reply_id" + end + + add_index "messages", ["author_id"], :name => "index_messages_on_author_id" + add_index "messages", ["board_id"], :name => "messages_board_id" + add_index "messages", ["created_on"], :name => "index_messages_on_created_on" + add_index "messages", ["last_reply_id"], :name => "index_messages_on_last_reply_id" + add_index "messages", ["parent_id"], :name => "messages_parent_id" + + create_table "news", :force => true do |t| + t.integer "project_id" + t.string "title", :limit => 60, :default => "", :null => false + t.string "summary", :default => "" + t.text "description" + t.integer "author_id", :default => 0, :null => false + t.datetime "created_on" + t.integer "comments_count", :default => 0, :null => false + t.integer "course_id" + end + + add_index "news", ["author_id"], :name => "index_news_on_author_id" + add_index "news", ["created_on"], :name => "index_news_on_created_on" + add_index "news", ["project_id"], :name => "news_project_id" + + create_table "no_uses", :force => true do |t| + t.integer "user_id", :null => false + t.string "no_use_type" + t.integer "no_use_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "notificationcomments", :force => true do |t| + t.string "notificationcommented_type" + t.integer "notificationcommented_id" + t.integer "author_id" + t.text "notificationcomments" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "onclick_times", :force => true do |t| + t.integer "user_id" + t.datetime "onclick_time" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "open_id_authentication_associations", :force => true do |t| + t.integer "issued" + t.integer "lifetime" + t.string "handle" + t.string "assoc_type" + t.binary "server_url" + t.binary "secret" + end + + create_table "open_id_authentication_nonces", :force => true do |t| + t.integer "timestamp", :null => false + t.string "server_url" + t.string "salt", :null => false + end + + create_table "open_source_projects", :force => true do |t| + t.string "name" + t.text "description" + t.integer "commit_count", :default => 0 + t.integer "code_line", :default => 0 + t.integer "users_count", :default => 0 + t.date "last_commit_time" + t.string "url" + t.date "date_collected" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "option_numbers", :force => true do |t| + t.integer "user_id" + t.integer "memo" + t.integer "messages_for_issues" + t.integer "issues_status" + t.integer "replay_for_message" + t.integer "replay_for_memo" + t.integer "follow" + t.integer "tread" + t.integer "praise_by_one" + t.integer "praise_by_two" + t.integer "praise_by_three" + t.integer "tread_by_one" + t.integer "tread_by_two" + t.integer "tread_by_three" + t.integer "changeset" + t.integer "document" + t.integer "attachment" + t.integer "issue_done_ratio" + t.integer "post_issue" + t.integer "score_type" + t.integer "total_score" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "project_id" + end + + create_table "organizations", :force => true do |t| + t.string "name" + t.string "logo_link" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "phone_app_versions", :force => true do |t| + t.string "version" + t.text "description" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "poll_answers", :force => true do |t| + t.integer "poll_question_id" + t.text "answer_text" + t.integer "answer_position" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "poll_questions", :force => true do |t| + t.string "question_title" + t.integer "question_type" + t.integer "is_necessary" + t.integer "poll_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "question_number" + end + + create_table "poll_users", :force => true do |t| + t.integer "user_id" + t.integer "poll_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "poll_votes", :force => true do |t| + t.integer "user_id" + t.integer "poll_question_id" + t.integer "poll_answer_id" + t.text "vote_text" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "polls", :force => true do |t| + t.string "polls_name" + t.string "polls_type" + t.integer "polls_group_id" + t.integer "polls_status" + t.integer "user_id" + t.datetime "published_at" + t.datetime "closed_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.text "polls_description" + t.integer "show_result", :default => 1 + end + + create_table "praise_tread_caches", :force => true do |t| + t.integer "object_id", :null => false + t.string "object_type" + t.integer "praise_num" + t.integer "tread_num" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "praise_treads", :force => true do |t| + t.integer "user_id", :null => false + t.integer "praise_tread_object_id" + t.string "praise_tread_object_type" + t.integer "praise_or_tread" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "principal_activities", :force => true do |t| + t.integer "user_id" + t.integer "principal_id" + t.integer "principal_act_id" + t.string "principal_act_type" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "project_infos", :force => true do |t| + t.integer "project_id" + t.integer "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "project_scores", :force => true do |t| + t.string "project_id" + t.integer "score" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "issue_num", :default => 0 + t.integer "issue_journal_num", :default => 0 + t.integer "news_num", :default => 0 + t.integer "documents_num", :default => 0 + t.integer "changeset_num", :default => 0 + t.integer "board_message_num", :default => 0 + end + + create_table "project_statuses", :force => true do |t| + t.integer "changesets_count" + t.integer "watchers_count" + t.integer "project_id" + t.integer "project_type" + t.float "grade", :default => 0.0 + t.integer "course_ac_para", :default => 0 + end + + add_index "project_statuses", ["grade"], :name => "index_project_statuses_on_grade" + + create_table "projecting_softapplictions", :force => true do |t| + t.integer "user_id" + t.integer "softapplication_id" + t.integer "project_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "projects", :force => true do |t| + t.string "name", :default => "", :null => false + t.text "description" + t.string "homepage", :default => "" + t.boolean "is_public", :default => true, :null => false + t.integer "parent_id" + t.datetime "created_on" + t.datetime "updated_on" + t.string "identifier" + t.integer "status", :default => 1, :null => false + t.integer "lft" + t.integer "rgt" + t.boolean "inherit_members", :default => false, :null => false + t.integer "project_type" + t.boolean "hidden_repo", :default => false, :null => false + t.integer "attachmenttype", :default => 1 + t.integer "user_id" + t.integer "dts_test", :default => 0 + 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" + add_index "projects", ["rgt"], :name => "index_projects_on_rgt" + + create_table "projects_trackers", :id => false, :force => true do |t| + t.integer "project_id", :default => 0, :null => false + t.integer "tracker_id", :default => 0, :null => false + end + + add_index "projects_trackers", ["project_id", "tracker_id"], :name => "projects_trackers_unique", :unique => true + add_index "projects_trackers", ["project_id"], :name => "projects_trackers_project_id" + + create_table "queries", :force => true do |t| + t.integer "project_id" + t.string "name", :default => "", :null => false + t.text "filters" + t.integer "user_id", :default => 0, :null => false + t.boolean "is_public", :default => false, :null => false + t.text "column_names" + t.text "sort_criteria" + t.string "group_by" + t.string "type" + end + + add_index "queries", ["project_id"], :name => "index_queries_on_project_id" + add_index "queries", ["user_id"], :name => "index_queries_on_user_id" + + create_table "relative_memo_to_open_source_projects", :force => true do |t| + t.integer "osp_id" + t.integer "relative_memo_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "relative_memos", :force => true do |t| + t.integer "osp_id" + t.integer "parent_id" + t.string "subject", :null => false + t.text "content", :limit => 16777215, :null => false + t.integer "author_id" + t.integer "replies_count", :default => 0 + t.integer "last_reply_id" + t.boolean "lock", :default => false + t.boolean "sticky", :default => false + t.boolean "is_quote", :default => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "viewed_count_crawl", :default => 0 + t.integer "viewed_count_local", :default => 0 + t.string "url" + t.string "username" + t.string "userhomeurl" + t.date "date_collected" + t.string "topic_resource" + end + + create_table "repositories", :force => true do |t| + t.integer "project_id", :default => 0, :null => false + t.string "url", :default => "", :null => false + t.string "login", :limit => 60, :default => "" + t.string "password", :default => "" + t.string "root_url", :default => "" + t.string "type" + t.string "path_encoding", :limit => 64 + t.string "log_encoding", :limit => 64 + t.text "extra_info" + t.string "identifier" + t.boolean "is_default", :default => false + t.boolean "hidden", :default => false + end + + add_index "repositories", ["project_id"], :name => "index_repositories_on_project_id" + + create_table "rich_rich_files", :force => true do |t| + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "rich_file_file_name" + t.string "rich_file_content_type" + t.integer "rich_file_file_size" + t.datetime "rich_file_updated_at" + t.string "owner_type" + t.integer "owner_id" + t.text "uri_cache" + t.string "simplified_type", :default => "file" + end + + create_table "roles", :force => true do |t| + t.string "name", :limit => 30, :default => "", :null => false + t.integer "position", :default => 1 + t.boolean "assignable", :default => true + t.integer "builtin", :default => 0, :null => false + t.text "permissions" + t.string "issues_visibility", :limit => 30, :default => "default", :null => false + end + + create_table "schools", :force => true do |t| + t.string "name" + t.string "province" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "logo_link" + t.string "pinyin" + end + + create_table "seems_rateable_cached_ratings", :force => true do |t| + t.integer "cacheable_id", :limit => 8 + t.string "cacheable_type" + t.float "avg", :null => false + t.integer "cnt", :null => false + t.string "dimension" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "seems_rateable_rates", :force => true do |t| + t.integer "rater_id", :limit => 8 + t.integer "rateable_id" + t.string "rateable_type" + t.float "stars", :null => false + t.string "dimension" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "is_teacher_score", :default => 0 + end + + create_table "settings", :force => true do |t| + t.string "name", :default => "", :null => false + t.text "value" + t.datetime "updated_on" + end + + add_index "settings", ["name"], :name => "index_settings_on_name" + + create_table "shares", :force => true do |t| + t.date "created_on" + t.string "url" + t.string "title" + t.integer "share_type" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "project_id" + t.integer "user_id" + t.string "description" + end + + create_table "softapplications", :force => true do |t| + t.string "name" + t.text "description" + t.integer "app_type_id" + t.string "app_type_name" + t.string "android_min_version_available" + t.integer "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "contest_id" + t.integer "softapplication_id" + t.integer "is_public" + t.string "application_developers" + t.string "deposit_project_url" + t.string "deposit_project" + t.integer "project_id" + end + + create_table "student_work_tests", :force => true do |t| + t.integer "student_work_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "status", :default => 9 + t.text "results" + t.text "src" + end + + create_table "student_works", :force => true do |t| + t.string "name" + t.text "description", :limit => 2147483647 + t.integer "homework_common_id" + t.integer "user_id" + t.float "final_score" + t.float "teacher_score" + t.float "student_score" + t.float "teaching_asistant_score" + t.integer "project_id", :default => 0 + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "late_penalty", :default => 0 + t.integer "absence_penalty", :default => 0 + t.float "system_score", :default => 0.0 + t.boolean "is_test", :default => false + end + + create_table "student_works_evaluation_distributions", :force => true do |t| + t.integer "student_work_id" + t.integer "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "student_works_scores", :force => true do |t| + t.integer "student_work_id" + t.integer "user_id" + t.integer "score" + t.text "comment" + t.integer "reviewer_role" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "students_for_courses", :force => true do |t| + t.integer "student_id" + t.integer "course_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "students_for_courses", ["course_id"], :name => "index_students_for_courses_on_course_id" + add_index "students_for_courses", ["student_id"], :name => "index_students_for_courses_on_student_id" + + create_table "system_messages", :force => true do |t| + t.integer "user_id" + t.string "content" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.text "description" + t.string "subject" + end + + create_table "taggings", :force => true do |t| + t.integer "tag_id" + t.integer "taggable_id" + t.string "taggable_type" + t.integer "tagger_id" + t.string "tagger_type" + t.string "context", :limit => 128 + t.datetime "created_at" + end + + add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id" + add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context" + add_index "taggings", ["taggable_type"], :name => "index_taggings_on_taggable_type" + + create_table "tags", :force => true do |t| + t.string "name" + end + + create_table "teachers", :force => true do |t| + t.string "tea_name" + t.string "location" + t.integer "couurse_time" + t.integer "course_code" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "extra" + end + + create_table "time_entries", :force => true do |t| + t.integer "project_id", :null => false + t.integer "user_id", :null => false + t.integer "issue_id" + t.float "hours", :null => false + t.string "comments" + t.integer "activity_id", :null => false + t.date "spent_on", :null => false + t.integer "tyear", :null => false + t.integer "tmonth", :null => false + t.integer "tweek", :null => false + t.datetime "created_on", :null => false + t.datetime "updated_on", :null => false + end + + add_index "time_entries", ["activity_id"], :name => "index_time_entries_on_activity_id" + add_index "time_entries", ["created_on"], :name => "index_time_entries_on_created_on" + add_index "time_entries", ["issue_id"], :name => "time_entries_issue_id" + add_index "time_entries", ["project_id"], :name => "time_entries_project_id" + add_index "time_entries", ["user_id"], :name => "index_time_entries_on_user_id" + + create_table "tokens", :force => true do |t| + t.integer "user_id", :default => 0, :null => false + t.string "action", :limit => 30, :default => "", :null => false + t.string "value", :limit => 40, :default => "", :null => false + t.datetime "created_on", :null => false + end + + add_index "tokens", ["user_id"], :name => "index_tokens_on_user_id" + add_index "tokens", ["value"], :name => "tokens_value", :unique => true + + create_table "trackers", :force => true do |t| + t.string "name", :limit => 30, :default => "", :null => false + t.boolean "is_in_chlog", :default => false, :null => false + t.integer "position", :default => 1 + t.boolean "is_in_roadmap", :default => true, :null => false + t.integer "fields_bits", :default => 0 + end + + create_table "user_activities", :force => true do |t| + t.string "act_type" + t.integer "act_id" + t.string "container_type" + t.integer "container_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "user_id" + end + + create_table "user_extensions", :force => true do |t| + t.integer "user_id", :null => false + t.date "birthday" + t.string "brief_introduction" + t.integer "gender" + t.string "location" + t.string "occupation" + t.integer "work_experience" + t.integer "zip_code" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "technical_title" + t.integer "identity" + t.string "student_id" + t.string "teacher_realname" + t.string "student_realname" + t.string "location_city" + t.integer "school_id" + t.string "description", :default => "" + end + + create_table "user_feedback_messages", :force => true do |t| + t.integer "user_id" + t.integer "journals_for_message_id" + t.string "journals_for_message_type" + t.integer "viewed" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "user_grades", :force => true do |t| + t.integer "user_id", :null => false + t.integer "project_id", :null => false + t.float "grade", :default => 0.0 + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "user_grades", ["grade"], :name => "index_user_grades_on_grade" + add_index "user_grades", ["project_id"], :name => "index_user_grades_on_project_id" + add_index "user_grades", ["user_id"], :name => "index_user_grades_on_user_id" + + create_table "user_levels", :force => true do |t| + t.integer "user_id" + t.integer "level" + end + + create_table "user_preferences", :force => true do |t| + t.integer "user_id", :default => 0, :null => false + t.text "others" + t.boolean "hide_mail", :default => false + t.string "time_zone" + end + + add_index "user_preferences", ["user_id"], :name => "index_user_preferences_on_user_id" + + create_table "user_score_details", :force => true do |t| + t.integer "current_user_id" + t.integer "target_user_id" + t.string "score_type" + t.string "score_action" + t.integer "user_id" + t.integer "old_score" + t.integer "new_score" + t.integer "current_user_level" + t.integer "target_user_level" + t.integer "score_changeable_obj_id" + t.string "score_changeable_obj_type" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "user_scores", :force => true do |t| + t.integer "user_id", :null => false + t.integer "collaboration" + t.integer "influence" + t.integer "skill" + t.integer "active" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "user_statuses", :force => true do |t| + t.integer "changesets_count" + t.integer "watchers_count" + t.integer "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.float "grade", :default => 0.0 + end + + add_index "user_statuses", ["changesets_count"], :name => "index_user_statuses_on_changesets_count" + add_index "user_statuses", ["grade"], :name => "index_user_statuses_on_grade" + add_index "user_statuses", ["watchers_count"], :name => "index_user_statuses_on_watchers_count" + + create_table "users", :force => true do |t| + t.string "login", :default => "", :null => false + t.string "hashed_password", :limit => 40, :default => "", :null => false + t.string "firstname", :limit => 30, :default => "", :null => false + t.string "lastname", :default => "", :null => false + t.string "mail", :limit => 60, :default => "", :null => false + t.boolean "admin", :default => false, :null => false + t.integer "status", :default => 1, :null => false + t.datetime "last_login_on" + t.string "language", :limit => 5, :default => "" + t.integer "auth_source_id" + t.datetime "created_on" + t.datetime "updated_on" + t.string "type" + 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" + add_index "users", ["id", "type"], :name => "index_users_on_id_and_type" + add_index "users", ["type"], :name => "index_users_on_type" + + create_table "versions", :force => true do |t| + t.integer "project_id", :default => 0, :null => false + t.string "name", :default => "", :null => false + t.string "description", :default => "" + t.date "effective_date" + t.datetime "created_on" + t.datetime "updated_on" + t.string "wiki_page_title" + t.string "status", :default => "open" + t.string "sharing", :default => "none", :null => false + end + + add_index "versions", ["project_id"], :name => "versions_project_id" + add_index "versions", ["sharing"], :name => "index_versions_on_sharing" + + create_table "visitors", :force => true do |t| + t.integer "user_id" + t.integer "master_id" + t.datetime "updated_on" + t.datetime "created_on" + end + + add_index "visitors", ["master_id"], :name => "index_visitors_master_id" + add_index "visitors", ["updated_on"], :name => "index_visitors_updated_on" + add_index "visitors", ["user_id"], :name => "index_visitors_user_id" + + create_table "watchers", :force => true do |t| + t.string "watchable_type", :default => "", :null => false + t.integer "watchable_id", :default => 0, :null => false + t.integer "user_id" + end + + add_index "watchers", ["user_id", "watchable_type"], :name => "watchers_user_id_type" + add_index "watchers", ["user_id"], :name => "index_watchers_on_user_id" + add_index "watchers", ["watchable_id", "watchable_type"], :name => "index_watchers_on_watchable_id_and_watchable_type" + + create_table "web_footer_companies", :force => true do |t| + t.string "name" + t.string "logo_size" + t.string "url" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "web_footer_oranizers", :force => true do |t| + t.string "name" + t.text "description" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "wiki_content_versions", :force => true do |t| + t.integer "wiki_content_id", :null => false + t.integer "page_id", :null => false + t.integer "author_id" + t.binary "data", :limit => 2147483647 + t.string "compression", :limit => 6, :default => "" + t.string "comments", :default => "" + t.datetime "updated_on", :null => false + t.integer "version", :null => false + end + + add_index "wiki_content_versions", ["updated_on"], :name => "index_wiki_content_versions_on_updated_on" + add_index "wiki_content_versions", ["wiki_content_id"], :name => "wiki_content_versions_wcid" + + create_table "wiki_contents", :force => true do |t| + t.integer "page_id", :null => false + t.integer "author_id" + t.text "text", :limit => 2147483647 + t.string "comments", :default => "" + t.datetime "updated_on", :null => false + t.integer "version", :null => false + end + + add_index "wiki_contents", ["author_id"], :name => "index_wiki_contents_on_author_id" + add_index "wiki_contents", ["page_id"], :name => "wiki_contents_page_id" + + create_table "wiki_pages", :force => true do |t| + t.integer "wiki_id", :null => false + t.string "title", :null => false + t.datetime "created_on", :null => false + t.boolean "protected", :default => false, :null => false + t.integer "parent_id" + end + + add_index "wiki_pages", ["parent_id"], :name => "index_wiki_pages_on_parent_id" + add_index "wiki_pages", ["wiki_id", "title"], :name => "wiki_pages_wiki_id_title" + add_index "wiki_pages", ["wiki_id"], :name => "index_wiki_pages_on_wiki_id" + + create_table "wiki_redirects", :force => true do |t| + t.integer "wiki_id", :null => false + t.string "title" + t.string "redirects_to" + t.datetime "created_on", :null => false + end + + add_index "wiki_redirects", ["wiki_id", "title"], :name => "wiki_redirects_wiki_id_title" + add_index "wiki_redirects", ["wiki_id"], :name => "index_wiki_redirects_on_wiki_id" + + create_table "wikis", :force => true do |t| + t.integer "project_id", :null => false + t.string "start_page", :null => false + t.integer "status", :default => 1, :null => false + end + + add_index "wikis", ["project_id"], :name => "wikis_project_id" + + create_table "workflows", :force => true do |t| + t.integer "tracker_id", :default => 0, :null => false + t.integer "old_status_id", :default => 0, :null => false + t.integer "new_status_id", :default => 0, :null => false + t.integer "role_id", :default => 0, :null => false + t.boolean "assignee", :default => false, :null => false + t.boolean "author", :default => false, :null => false + t.string "type", :limit => 30 + t.string "field_name", :limit => 30 + t.string "rule", :limit => 30 + end + + add_index "workflows", ["new_status_id"], :name => "index_workflows_on_new_status_id" + add_index "workflows", ["old_status_id"], :name => "index_workflows_on_old_status_id" + add_index "workflows", ["role_id", "tracker_id", "old_status_id"], :name => "wkfs_role_tracker_old_status" + add_index "workflows", ["role_id"], :name => "index_workflows_on_role_id" + + create_table "works_categories", :force => true do |t| + t.string "category" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "zip_packs", :force => true do |t| + t.integer "user_id" + t.integer "homework_id" + t.string "file_digest" + t.string "file_path" + t.integer "pack_times", :default => 1 + t.integer "pack_size", :default => 0 + t.text "file_digests" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + +end diff --git a/lib/gitlab-cli/Gemfile b/lib/gitlab-cli/Gemfile new file mode 100644 index 000000000..54fbdf177 --- /dev/null +++ b/lib/gitlab-cli/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in gitlab.gemspec +gemspec diff --git a/lib/gitlab-cli/LICENSE.txt b/lib/gitlab-cli/LICENSE.txt new file mode 100644 index 000000000..2c289176d --- /dev/null +++ b/lib/gitlab-cli/LICENSE.txt @@ -0,0 +1,24 @@ +Copyright (c) 2012-2014 Nihad Abbasov +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. diff --git a/lib/gitlab-cli/README.md b/lib/gitlab-cli/README.md new file mode 100644 index 000000000..1aa73996c --- /dev/null +++ b/lib/gitlab-cli/README.md @@ -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) +# => [#1, "code"=>"brute", "name"=>"Brute", "description"=>nil, "path"=>"brute", "default_branch"=>nil, "owner"=>#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"}>, #2, "code"=>"mozart", "name"=>"Mozart", "description"=>nil, "path"=>"mozart", "default_branch"=>nil, "owner"=>#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"}>, #3, "code"=>"gitlab", "name"=>"Gitlab", "description"=>nil, "path"=>"gitlab", "default_branch"=>nil, "owner"=>#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') +# => # + +# get a user +user = g.user +# => #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. diff --git a/lib/gitlab-cli/Rakefile b/lib/gitlab-cli/Rakefile new file mode 100644 index 000000000..bf451bdcb --- /dev/null +++ b/lib/gitlab-cli/Rakefile @@ -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 diff --git a/lib/gitlab-cli/bin/gitlab b/lib/gitlab-cli/bin/gitlab new file mode 100644 index 000000000..af0fd0cc0 --- /dev/null +++ b/lib/gitlab-cli/bin/gitlab @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +$:.unshift File.expand_path('../../lib', __FILE__) + +require 'gitlab/cli' + +Gitlab::CLI.start(ARGV) diff --git a/lib/gitlab-cli/gitlab.gemspec b/lib/gitlab-cli/gitlab.gemspec new file mode 100644 index 000000000..7d5f30862 --- /dev/null +++ b/lib/gitlab-cli/gitlab.gemspec @@ -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 diff --git a/lib/gitlab-cli/lib/gitlab.rb b/lib/gitlab-cli/lib/gitlab.rb new file mode 100644 index 000000000..95c382678 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab.rb @@ -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] + 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 diff --git a/lib/gitlab-cli/lib/gitlab/api.rb b/lib/gitlab-cli/lib/gitlab/api.rb new file mode 100644 index 000000000..62b3f47dc --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/api.rb @@ -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 diff --git a/lib/gitlab-cli/lib/gitlab/cli.rb b/lib/gitlab-cli/lib/gitlab/cli.rb new file mode 100644 index 000000000..57c2aa5b4 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/cli.rb @@ -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 diff --git a/lib/gitlab-cli/lib/gitlab/cli_helpers.rb b/lib/gitlab-cli/lib/gitlab/cli_helpers.rb new file mode 100644 index 000000000..d1c777dc0 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/cli_helpers.rb @@ -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 diff --git a/lib/gitlab-cli/lib/gitlab/client.rb b/lib/gitlab-cli/lib/gitlab/client.rb new file mode 100644 index 000000000..5cb01ce2b --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/client.rb @@ -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 diff --git a/lib/gitlab-cli/lib/gitlab/client/branches.rb b/lib/gitlab-cli/lib/gitlab/client/branches.rb new file mode 100644 index 000000000..0ba3afb29 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/client/branches.rb @@ -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] + 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 + diff --git a/lib/gitlab-cli/lib/gitlab/client/groups.rb b/lib/gitlab-cli/lib/gitlab/client/groups.rb new file mode 100644 index 000000000..1ea4bf5bc --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/client/groups.rb @@ -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] + 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] + 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 diff --git a/lib/gitlab-cli/lib/gitlab/client/issues.rb b/lib/gitlab-cli/lib/gitlab/client/issues.rb new file mode 100644 index 000000000..7248df848 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/client/issues.rb @@ -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] + 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 diff --git a/lib/gitlab-cli/lib/gitlab/client/merge_requests.rb b/lib/gitlab-cli/lib/gitlab/client/merge_requests.rb new file mode 100644 index 000000000..1ccc25081 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/client/merge_requests.rb @@ -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] + 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 '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 diff --git a/lib/gitlab-cli/lib/gitlab/client/milestones.rb b/lib/gitlab-cli/lib/gitlab/client/milestones.rb new file mode 100644 index 000000000..4ba4d5e87 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/client/milestones.rb @@ -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] + 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 diff --git a/lib/gitlab-cli/lib/gitlab/client/notes.rb b/lib/gitlab-cli/lib/gitlab/client/notes.rb new file mode 100644 index 000000000..7d782e8e2 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/client/notes.rb @@ -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] + 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] + 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] + 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 diff --git a/lib/gitlab-cli/lib/gitlab/client/projects.rb b/lib/gitlab-cli/lib/gitlab/client/projects.rb new file mode 100644 index 000000000..04ea682aa --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/client/projects.rb @@ -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] + 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] + 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] + 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] 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] + 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] + 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 diff --git a/lib/gitlab-cli/lib/gitlab/client/repositories.rb b/lib/gitlab-cli/lib/gitlab/client/repositories.rb new file mode 100644 index 000000000..f489e5009 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/client/repositories.rb @@ -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] + 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] + 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 diff --git a/lib/gitlab-cli/lib/gitlab/client/snippets.rb b/lib/gitlab-cli/lib/gitlab/client/snippets.rb new file mode 100644 index 000000000..594d37402 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/client/snippets.rb @@ -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 diff --git a/lib/gitlab-cli/lib/gitlab/client/system_hooks.rb b/lib/gitlab-cli/lib/gitlab/client/system_hooks.rb new file mode 100644 index 000000000..59db4f924 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/client/system_hooks.rb @@ -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] + 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] + 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 diff --git a/lib/gitlab-cli/lib/gitlab/client/users.rb b/lib/gitlab-cli/lib/gitlab/client/users.rb new file mode 100644 index 000000000..3fc83cd1b --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/client/users.rb @@ -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] + 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] + 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 diff --git a/lib/gitlab-cli/lib/gitlab/configuration.rb b/lib/gitlab-cli/lib/gitlab/configuration.rb new file mode 100644 index 000000000..b36ed5a7d --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/configuration.rb @@ -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 diff --git a/lib/gitlab-cli/lib/gitlab/error.rb b/lib/gitlab-cli/lib/gitlab/error.rb new file mode 100644 index 000000000..96a8a39d6 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/error.rb @@ -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 diff --git a/lib/gitlab-cli/lib/gitlab/help.rb b/lib/gitlab-cli/lib/gitlab/help.rb new file mode 100644 index 000000000..6ead8af94 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/help.rb @@ -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 diff --git a/lib/gitlab-cli/lib/gitlab/objectified_hash.rb b/lib/gitlab-cli/lib/gitlab/objectified_hash.rb new file mode 100644 index 000000000..87ca2fc77 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/objectified_hash.rb @@ -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 diff --git a/lib/gitlab-cli/lib/gitlab/request.rb b/lib/gitlab-cli/lib/gitlab/request.rb new file mode 100644 index 000000000..64a8f6b4c --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/request.rb @@ -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 diff --git a/lib/gitlab-cli/lib/gitlab/shell.rb b/lib/gitlab-cli/lib/gitlab/shell.rb new file mode 100644 index 000000000..9253b61e3 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/shell.rb @@ -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 diff --git a/lib/gitlab-cli/lib/gitlab/version.rb b/lib/gitlab-cli/lib/gitlab/version.rb new file mode 100644 index 000000000..42ba291e0 --- /dev/null +++ b/lib/gitlab-cli/lib/gitlab/version.rb @@ -0,0 +1,3 @@ +module Gitlab + VERSION = "3.2.0" +end diff --git a/lib/gitlab-cli/spec/fixtures/branch.json b/lib/gitlab-cli/spec/fixtures/branch.json new file mode 100644 index 000000000..34a02081f --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/branch.json @@ -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} diff --git a/lib/gitlab-cli/spec/fixtures/branches.json b/lib/gitlab-cli/spec/fixtures/branches.json new file mode 100644 index 000000000..05a39447d --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/branches.json @@ -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"}}] diff --git a/lib/gitlab-cli/spec/fixtures/comment_merge_request.json b/lib/gitlab-cli/spec/fixtures/comment_merge_request.json new file mode 100644 index 000000000..742f33377 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/comment_merge_request.json @@ -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"}} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/create_branch.json b/lib/gitlab-cli/spec/fixtures/create_branch.json new file mode 100644 index 000000000..0b011abdb --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/create_branch.json @@ -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} diff --git a/lib/gitlab-cli/spec/fixtures/create_merge_request.json b/lib/gitlab-cli/spec/fixtures/create_merge_request.json new file mode 100644 index 000000000..c27435168 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/create_merge_request.json @@ -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"}} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/error_already_exists.json b/lib/gitlab-cli/spec/fixtures/error_already_exists.json new file mode 100644 index 000000000..d1070f560 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/error_already_exists.json @@ -0,0 +1 @@ +{"message": "409 Already exists"} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/group.json b/lib/gitlab-cli/spec/fixtures/group.json new file mode 100644 index 000000000..bce3581d8 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/group.json @@ -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" + } + } +]} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/group_create.json b/lib/gitlab-cli/spec/fixtures/group_create.json new file mode 100644 index 000000000..67445f68b --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/group_create.json @@ -0,0 +1 @@ +{"id":3,"name":"Gitlab-Group","path":"gitlab-group","owner_id":1} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/group_member.json b/lib/gitlab-cli/spec/fixtures/group_member.json new file mode 100644 index 000000000..feef54322 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/group_member.json @@ -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} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/group_member_delete.json b/lib/gitlab-cli/spec/fixtures/group_member_delete.json new file mode 100644 index 000000000..ff052edf6 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/group_member_delete.json @@ -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} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/group_members.json b/lib/gitlab-cli/spec/fixtures/group_members.json new file mode 100644 index 000000000..02ddc1089 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/group_members.json @@ -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}] \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/groups.json b/lib/gitlab-cli/spec/fixtures/groups.json new file mode 100644 index 000000000..7d8b426a4 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/groups.json @@ -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} +] \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/issue.json b/lib/gitlab-cli/spec/fixtures/issue.json new file mode 100644 index 000000000..9f70318a7 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/issue.json @@ -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"} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/issues.json b/lib/gitlab-cli/spec/fixtures/issues.json new file mode 100644 index 000000000..62e4cadd2 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/issues.json @@ -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"}] \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/key.json b/lib/gitlab-cli/spec/fixtures/key.json new file mode 100644 index 000000000..6595c8ceb --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/key.json @@ -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"} diff --git a/lib/gitlab-cli/spec/fixtures/keys.json b/lib/gitlab-cli/spec/fixtures/keys.json new file mode 100644 index 000000000..d81fca6ad --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/keys.json @@ -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"}] diff --git a/lib/gitlab-cli/spec/fixtures/merge_request.json b/lib/gitlab-cli/spec/fixtures/merge_request.json new file mode 100644 index 000000000..5278f4664 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/merge_request.json @@ -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"}} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/merge_request_comments.json b/lib/gitlab-cli/spec/fixtures/merge_request_comments.json new file mode 100644 index 000000000..3b9733ef3 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/merge_request_comments.json @@ -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"}}] diff --git a/lib/gitlab-cli/spec/fixtures/merge_requests.json b/lib/gitlab-cli/spec/fixtures/merge_requests.json new file mode 100644 index 000000000..ea32ac4e6 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/merge_requests.json @@ -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"}}] diff --git a/lib/gitlab-cli/spec/fixtures/milestone.json b/lib/gitlab-cli/spec/fixtures/milestone.json new file mode 100644 index 000000000..94ea3d360 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/milestone.json @@ -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"} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/milestones.json b/lib/gitlab-cli/spec/fixtures/milestones.json new file mode 100644 index 000000000..f9e309af6 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/milestones.json @@ -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"}] \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/note.json b/lib/gitlab-cli/spec/fixtures/note.json new file mode 100644 index 000000000..3f575aed3 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/note.json @@ -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"} diff --git a/lib/gitlab-cli/spec/fixtures/notes.json b/lib/gitlab-cli/spec/fixtures/notes.json new file mode 100644 index 000000000..15c0d8ca2 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/notes.json @@ -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"}] diff --git a/lib/gitlab-cli/spec/fixtures/project.json b/lib/gitlab-cli/spec/fixtures/project.json new file mode 100644 index 000000000..1f4f96028 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project.json @@ -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"} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/project_commit.json b/lib/gitlab-cli/spec/fixtures/project_commit.json new file mode 100644 index 000000000..ace52b700 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_commit.json @@ -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" + ] +} diff --git a/lib/gitlab-cli/spec/fixtures/project_commit_diff.json b/lib/gitlab-cli/spec/fixtures/project_commit_diff.json new file mode 100644 index 000000000..ad3dfde86 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_commit_diff.json @@ -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 +} diff --git a/lib/gitlab-cli/spec/fixtures/project_commits.json b/lib/gitlab-cli/spec/fixtures/project_commits.json new file mode 100644 index 000000000..58cb5020d --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_commits.json @@ -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"}] \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/project_delete_key.json b/lib/gitlab-cli/spec/fixtures/project_delete_key.json new file mode 100644 index 000000000..ed4141599 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_delete_key.json @@ -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 +} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/project_events.json b/lib/gitlab-cli/spec/fixtures/project_events.json new file mode 100644 index 000000000..4d5afc04e --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_events.json @@ -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"}] diff --git a/lib/gitlab-cli/spec/fixtures/project_for_user.json b/lib/gitlab-cli/spec/fixtures/project_for_user.json new file mode 100644 index 000000000..e2835d865 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_for_user.json @@ -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"} diff --git a/lib/gitlab-cli/spec/fixtures/project_fork_link.json b/lib/gitlab-cli/spec/fixtures/project_fork_link.json new file mode 100644 index 000000000..f1490dfa7 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_fork_link.json @@ -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"} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/project_hook.json b/lib/gitlab-cli/spec/fixtures/project_hook.json new file mode 100644 index 000000000..180dd4555 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_hook.json @@ -0,0 +1 @@ +{"id":1,"url":"https://api.example.net/v1/webhooks/ci"} diff --git a/lib/gitlab-cli/spec/fixtures/project_hooks.json b/lib/gitlab-cli/spec/fixtures/project_hooks.json new file mode 100644 index 000000000..e70d4122c --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_hooks.json @@ -0,0 +1 @@ +[{"id":1,"url":"https://api.example.net/v1/webhooks/ci"}] \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/project_issues.json b/lib/gitlab-cli/spec/fixtures/project_issues.json new file mode 100644 index 000000000..87fb2fb18 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_issues.json @@ -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"}] \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/project_key.json b/lib/gitlab-cli/spec/fixtures/project_key.json new file mode 100644 index 000000000..d917f9466 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_key.json @@ -0,0 +1,6 @@ +{ + "id": 2, + "title": "Key Title", + "key": "ssh-rsa ...", + "created_at": "2013-09-22T18:34:32Z" +} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/project_keys.json b/lib/gitlab-cli/spec/fixtures/project_keys.json new file mode 100644 index 000000000..dd22f9668 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_keys.json @@ -0,0 +1,6 @@ +[{ + "id": 2, + "title": "Key Title", + "key": "ssh-rsa ...", + "created_at": "2013-09-22T18:34:32Z" +}] \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/project_tags.json b/lib/gitlab-cli/spec/fixtures/project_tags.json new file mode 100644 index 000000000..1e2fb96cb --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/project_tags.json @@ -0,0 +1 @@ +[{"name":"v2.8.2","commit":{"id":"a502f67c0b358cc6b391df0c5dca48375c21fcad","parents":[{"id":"4381084af341684240b1a671d368511afcf5774a"}],"tree":"1612068bdd20de5d14b3096cfa4c621e2051ed4c","message":"Up to 2.8.2","author":{"name":"randx","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"randx","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-08-24T02:03:48-07:00","committed_date":"2012-08-24T02:03:48-07:00"}},{"name":"v2.8.1","commit":{"id":"ed2b53cd1c34c421b23208eeb502a141a6829f9d","parents":[{"id":"7ab587a47791e371f5c109c14097a5d1d7776ea5"}],"tree":"b7393b0b33b777583b285e85b423c4e5ab7f859f","message":"Up to 2.8.1","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-08-22T23:17:18-07:00","committed_date":"2012-08-22T23:17:18-07:00"}},{"name":"v2.8.0pre","commit":{"id":"b2c6ba97a25d299e83c51493d7bc770c13b8ed1a","parents":[{"id":"05da3801f53f06fdc529b4f3820af1380039f245"},{"id":"66399d558da45fb9cd3ea972a47a4f7bb12bfc8d"}],"tree":"36ad53f35bce1fe3f2a4a5f840e7b1bdbfed9c82","message":"Merge pull request #1230 from tsigo/hooray_apostrophes\n\nCorrect usage of \"Can't\"","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-08-16T14:11:08-07:00","committed_date":"2012-08-16T14:11:08-07:00"}},{"name":"v2.8.0","commit":{"id":"5c7ed6fa26b47ac71ff6ba04720d85df6d74b200","parents":[{"id":"d1daeba1736ba145fe525ce08a91f29495a3abf1"}],"tree":"4fc230ff2dbc0e75f27321eac2976aba5a6d323d","message":"Up to 2.8","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-08-21T15:15:26-07:00","committed_date":"2012-08-21T15:15:26-07:00"}},{"name":"v2.7.0pre","commit":{"id":"72a571724d84d112f98a5543c971e9b3b9da1383","parents":[{"id":"3ac840ff06e0ee5b349c52b5a8c02e803a17eec3"},{"id":"990b9217d9a55e26a53d4143d4a3c89123384327"}],"tree":"64b104df5d956e21e0749dc8e70849d1989de36f","message":"Merge pull request #1096 from moregeek/show-flash-note-when-destroying-a-project\n\nshow flash notice after deletion of a project","author":{"name":"Valeriy Sizov","email":"vsv2711@gmail.com"},"committer":{"name":"Valeriy Sizov","email":"vsv2711@gmail.com"},"authored_date":"2012-07-18T05:35:42-07:00","committed_date":"2012-07-18T05:35:42-07:00"}},{"name":"v2.7.0","commit":{"id":"8b7e404b5b6944e9c92cc270b2e5d0005781d49d","parents":[{"id":"11721b0dbe82c35789be3e4fa8e14663934b2ff5"}],"tree":"89fe8c5ff58daaedea07a910cffb14b04ebcc828","message":"Up to 2.7.0","author":{"name":"randx","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"randx","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-07-21T00:53:55-07:00","committed_date":"2012-07-21T00:53:55-07:00"}},{"name":"v2.6.3","commit":{"id":"666cdb22792dd955a286b9993d6235b4cdd68b4b","parents":[{"id":"d92446df1fdba87101c92c90b1c34eb2f1eebef4"}],"tree":"888173aa4c12a4920d318c35b950095d3505673d","message":"up to 2.6.3","author":{"name":"randx","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"randx","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-06-26T09:20:47-07:00","committed_date":"2012-06-26T09:21:28-07:00"}},{"name":"v2.6.2","commit":{"id":"39fecb554f172a0c8ea00316e612e1d37efc7200","parents":[{"id":"68389588d664100590b1a6ca7eedd50860b7e9bc"}],"tree":"53accb25e0b9d038d550cf387753bde15fe4ad19","message":"Up to 2.6.2","author":{"name":"randx","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"randx","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-06-22T13:50:58-07:00","committed_date":"2012-06-22T13:50:58-07:00"}},{"name":"v2.6.1","commit":{"id":"d92a22c9e627268eca697bbd9b660d8c335df953","parents":[{"id":"193804516b8b0783c850981456e947f888ff51bb"}],"tree":"4ac1b5225f597ab55372cb5e950b121d6f55e386","message":"Up to 2.6.1","author":{"name":"randx","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"randx","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-06-22T12:49:03-07:00","committed_date":"2012-06-22T12:49:03-07:00"}},{"name":"v2.6.0","commit":{"id":"b32465712becfbcf83d63b1e6eff7d1483fdabea","parents":[{"id":"1903f6ade027df0f10ef96b9439495eeda07482c"}],"tree":"ffbc05fd0f1771c1602c956df9556260048c7167","message":"Up to 2.6","author":{"name":"randx","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"randx","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-06-21T10:25:23-07:00","committed_date":"2012-06-21T10:25:23-07:00"}},{"name":"v2.5.0","commit":{"id":"cc8369144db2147d2956e8dd7d314e9a7dfd4fbb","parents":[{"id":"1b2068eaa91e5002d01a220c65da21dad8ccb071"}],"tree":"666a442e00689911169e8cc336c5ce60d014854c","message":"Prevent app crash in case if encoding failed","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-05-22T04:57:04-07:00","committed_date":"2012-05-22T04:57:04-07:00"}},{"name":"v2.4.2","commit":{"id":"f18339c26d673c5f8b4c19776036fd42a0de30aa","parents":[{"id":"c937d06c3c98e9ffce8ec1132203eaff6bf7b231"},{"id":"35e602f19c83585d64aa2043ed26eeb8cd7b40e2"}],"tree":"5101f0cd8e395fee1996764298a202437757e85b","message":"Merge branch 'master' of github.com:gitlabhq/gitlabhq","author":{"name":"Zevs","email":"vsv2711@gmail.com"},"committer":{"name":"Zevs","email":"vsv2711@gmail.com"},"authored_date":"2012-04-29T14:24:59-07:00","committed_date":"2012-04-29T14:24:59-07:00"}},{"name":"v2.4.1","commit":{"id":"d97a9aa4a44ff9f452144fad348fd9d7e3b48260","parents":[{"id":"21f3da23589d50038728393f0badc6255b5762ca"}],"tree":"905c33874b064778199f806749d5688e33d64be3","message":"fixed email markdown","author":{"name":"gitlabhq","email":"m@gitlabhq.com"},"committer":{"name":"gitlabhq","email":"m@gitlabhq.com"},"authored_date":"2012-04-23T05:32:56-07:00","committed_date":"2012-04-23T05:32:56-07:00"}},{"name":"v2.4.0pre","commit":{"id":"1845429268364e75bffdeb1075de8f1606e157ec","parents":[{"id":"45b18365d5f409f196a02a4e6e2b77b8ebef909b"}],"tree":"423c70246fa7ffd8804b26628fea34bdb2b22846","message":"Use try for commit prev_commit_id detection","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-04-19T13:35:35-07:00","committed_date":"2012-04-19T13:35:35-07:00"}},{"name":"v2.4.0","commit":{"id":"204c66461ed519eb0078be7e8ac8a6cb56834753","parents":[{"id":"511d07c47c9bf3a18bfa276d452c899369432a22"}],"tree":"9416c777cccf87d348f5705078e82f3f97485e19","message":"corrected exception for automerge","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-04-22T06:49:45-07:00","committed_date":"2012-04-22T06:49:45-07:00"}},{"name":"v2.3.1","commit":{"id":"fa8219e0a753e642a6f1dbdfc010d01ae8a949ee","parents":[{"id":"81da8e46f24913ccf42d3e2644962cbcbc0f9c2e"}],"tree":"5debfcd6d17f9d582aace6ac9b80db27d5c1fe36","message":"better MR list, dashboard pollished","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-03-22T13:57:04-07:00","committed_date":"2012-03-22T13:57:04-07:00"}},{"name":"v2.3.0pre","commit":{"id":"cadf12c60cc27c5b0b8273c1de4b190a0e88bd7d","parents":[{"id":"724ea16c348bc61cf7cb3dbe362c6f30cff1b2c7"}],"tree":"6f4c22761fd2dee405d3fbf38f9dd835bb3c8694","message":"Merged activities & projects pages","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-03-19T15:05:35-07:00","committed_date":"2012-03-19T15:05:35-07:00"}},{"name":"v2.3.0","commit":{"id":"b57faf9282d7df6cdd62953d474652a0ae2e6896","parents":[{"id":"cadf12c60cc27c5b0b8273c1de4b190a0e88bd7d"}],"tree":"f0d5b826df373191b4681452fc2ae4c5970cef4a","message":"Push events polished","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-03-20T14:59:35-07:00","committed_date":"2012-03-20T14:59:35-07:00"}},{"name":"v2.2.0pre","commit":{"id":"6a445b42003007cbb6c06f477c4d7a0b175688c1","parents":[{"id":"22f4c1908d0fc2dbce02e74ed03bf65f028d78d6"}],"tree":"9c60577833f6ca717acdebfa66140124c88e8471","message":"fixed forgot password form","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-02-20T10:37:37-08:00","committed_date":"2012-02-20T10:37:37-08:00"}},{"name":"v2.2.0","commit":{"id":"9e6d0710e927aa8ea834b8a9ae9f277be617ac7d","parents":[{"id":"8c40aab120dbc5507ab9cc8d7ad8e2519d6e9f25"},{"id":"6ea87c47f0f8a24ae031c3fff17bc913889ecd00"}],"tree":"86c831ab21236f21ffa5b97c752369612ce41b39","message":"Merge pull request #443 from CedricGatay/fix/incorrectLineNumberingInDiff\n\nIncorrect line numbering in diff","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-02-22T07:14:54-08:00","committed_date":"2012-02-22T07:14:54-08:00"}},{"name":"v2.1.0","commit":{"id":"98d6492582d232ed86525aa31ccbf280f4cbdaef","parents":[{"id":"611c5a87ab0c083a43785323b09cc47f554c3ba4"}],"tree":"1689b3cad580a18fd9b429ee0b33dac21c9f5a48","message":"removed broken migration","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2012-01-22T10:52:06-08:00","committed_date":"2012-01-22T10:52:06-08:00"}},{"name":"v2.0.0","commit":{"id":"9a2a8612769d472503b367fa35e99f6fb2876704","parents":[{"id":"2f7b67161952fc9ab322eba6878511b5f2dd5cf1"}],"tree":"26cdb4e66b5e664fe4bcd57d011c54c9c9c26ded","message":"Design tab for profile. Colorscheme as db value","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2011-12-20T12:47:09-08:00","committed_date":"2011-12-20T12:47:09-08:00"}},{"name":"v1.2.2","commit":{"id":"139a332293b9d8c4e5436619036e093483d8347f","parents":[{"id":"ade12da9488bea19d12505c371ead35686a1436e"}],"tree":"365d57f4df5c5dcac69b666cf6d2bfd8ef058008","message":"updated readme","author":{"name":"Dmitriy Zaporozhets","email":"dzaporozhets@sphereconsultinginc.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dzaporozhets@sphereconsultinginc.com"},"authored_date":"2011-11-25T14:30:51-08:00","committed_date":"2011-11-25T14:30:51-08:00"}},{"name":"v1.2.1","commit":{"id":"7ebba27db21719c0035bab65fea92a4780051c73","parents":[{"id":"b56024100d40457a998f83adae3cdc830c997cda"},{"id":"a4fbe13fce87cb6ff2a27a2574ae25bf1dad145c"}],"tree":"b121a7576af1503a96954ce9a94598a68579e053","message":"Merge branch 'master' of dev.gitlabhq.com:gitlabhq","author":{"name":"Dmitriy Zaporozhets","email":"dzaporozhets@sphereconsultinginc.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dzaporozhets@sphereconsultinginc.com"},"authored_date":"2011-11-22T13:15:09-08:00","committed_date":"2011-11-22T13:15:09-08:00"}},{"name":"v1.2.0pre","commit":{"id":"86829cae50857b5edf74b935380c6f68a19c2282","parents":[{"id":"a6b99319381c2d62ec4b92d64805e8de8965859e"}],"tree":"6aab9d13000584fa96fb3cb34d94f3b122bd1143","message":"fixed min height for menu","author":{"name":"gitlabhq","email":"m@gitlabhq.com"},"committer":{"name":"gitlabhq","email":"m@gitlabhq.com"},"authored_date":"2011-11-22T06:03:27-08:00","committed_date":"2011-11-22T06:03:27-08:00"}},{"name":"v1.2.0","commit":{"id":"b56024100d40457a998f83adae3cdc830c997cda","parents":[{"id":"4451b8df8ad6d4b6d79fbce77687c6c2fd37d0a9"}],"tree":"f402cbb6d54526a32b30968c98410bae97b27c8d","message":"lil style fixes","author":{"name":"Dmitriy Zaporozhets","email":"dzaporozhets@sphereconsultinginc.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dzaporozhets@sphereconsultinginc.com"},"authored_date":"2011-11-22T09:57:25-08:00","committed_date":"2011-11-22T09:57:25-08:00"}},{"name":"v1.1.0pre","commit":{"id":"6b030fd41d697e327d2935b406cba70b6a460504","parents":[{"id":"3a2b273316fb29d63b489906f85d9b5329377258"}],"tree":"63b1fdb2a0f135f7074f6a94da14543b8450dd71","message":"1.1pre1","author":{"name":"Dmitriy Zaporozhets","email":"dzaporozhets@sphereconsultinginc.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dzaporozhets@sphereconsultinginc.com"},"authored_date":"2011-10-21T10:04:41-07:00","committed_date":"2011-10-21T10:04:41-07:00"}},{"name":"v1.1.0","commit":{"id":"ba8048d71019b5aaa1f92ee5c3415bfddaa9babb","parents":[{"id":"6b030fd41d697e327d2935b406cba70b6a460504"}],"tree":"4db2b5f4f9b374dd1be3579459bc5947c225c9ba","message":"v1.1.0","author":{"name":"Dmitriy Zaporozhets","email":"dzaporozhets@sphereconsultinginc.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dzaporozhets@sphereconsultinginc.com"},"authored_date":"2011-10-22T06:07:26-07:00","committed_date":"2011-10-22T06:07:26-07:00"}},{"name":"v1.0.2","commit":{"id":"3a2b273316fb29d63b489906f85d9b5329377258","parents":[{"id":"757ea634665e475bf69c1ec962040a0511ee8aeb"},{"id":"c374eb80ff9fb71d37faffc15714bf98b632d3e5"}],"tree":"e0d8170e61a9468a7bb5d4e63305171ec1efa6bf","message":"Merge pull request #40 from vslinko/patch-1\n\nIncrease max key length. Some keys has comment after key string.","author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"authored_date":"2011-10-18T23:30:06-07:00","committed_date":"2011-10-18T23:30:06-07:00"}},{"name":"v1.0.1","commit":{"id":"7b5799a97998b68416f1b6233ce427135c99165a","parents":[{"id":"0541b3f3c5dcd291d144c83d9731c75ee811b4e0"},{"id":"7b67480c76db8b9a9ccdc80015cc500dc6d26892"}],"tree":"e052185e9dd72a1b1a04d59a5f9efbf3c0369601","message":"Merge branch '1x' of github.com:gitlabhq/gitlabhq into 1x","author":{"name":"Dmitriy Zaporozhets","email":"dzaporozhets@sphereconsultinginc.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dzaporozhets@sphereconsultinginc.com"},"authored_date":"2011-10-14T15:16:44-07:00","committed_date":"2011-10-14T15:16:44-07:00"}}] \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/projects.json b/lib/gitlab-cli/spec/fixtures/projects.json new file mode 100644 index 000000000..deab4c5f3 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/projects.json @@ -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 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"},{"id":2,"code":"mozart","name":"Mozart","description":null,"path":"mozart","default_branch":null,"owner":{"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"},{"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"},"private":true,"issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":true,"wiki_enabled":true,"created_at":"2012-09-17T09:41:58Z"}] \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/protect_branch.json b/lib/gitlab-cli/spec/fixtures/protect_branch.json new file mode 100644 index 000000000..752bc2389 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/protect_branch.json @@ -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} diff --git a/lib/gitlab-cli/spec/fixtures/session.json b/lib/gitlab-cli/spec/fixtures/session.json new file mode 100644 index 000000000..e4f5ba35f --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/session.json @@ -0,0 +1 @@ +{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z","private_token":"qEsq1pt6HJPaNciie3MG"} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/snippet.json b/lib/gitlab-cli/spec/fixtures/snippet.json new file mode 100644 index 000000000..34e9d994d --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/snippet.json @@ -0,0 +1 @@ +{"id":1,"title":"Rails Console ActionMailer","file_name":"mailer_test.rb","author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"expires_at":"2012-09-24T00:00:00Z","updated_at":"2012-09-17T09:51:42Z","created_at":"2012-09-17T09:51:42Z"} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/snippets.json b/lib/gitlab-cli/spec/fixtures/snippets.json new file mode 100644 index 000000000..26457995c --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/snippets.json @@ -0,0 +1 @@ +[{"id":1,"title":"Rails Console ActionMailer","file_name":"mailer_test.rb","author":{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z"},"expires_at":"2012-09-24T00:00:00Z","updated_at":"2012-09-17T09:51:42Z","created_at":"2012-09-17T09:51:42Z"}] diff --git a/lib/gitlab-cli/spec/fixtures/system_hook.json b/lib/gitlab-cli/spec/fixtures/system_hook.json new file mode 100644 index 000000000..0028b7a52 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/system_hook.json @@ -0,0 +1 @@ +{"id": 3, "url": "http://example.com/hook", "created_at": "2013-10-02T10:15:31Z"} diff --git a/lib/gitlab-cli/spec/fixtures/system_hook_test.json b/lib/gitlab-cli/spec/fixtures/system_hook_test.json new file mode 100644 index 000000000..cc79044ff --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/system_hook_test.json @@ -0,0 +1 @@ +{ "event_name": "project_create", "name": "Ruby", "path": "ruby", "project_id": 1, "owner_name": "Someone", "owner_email": "example@gitlabhq.com" } diff --git a/lib/gitlab-cli/spec/fixtures/system_hooks.json b/lib/gitlab-cli/spec/fixtures/system_hooks.json new file mode 100644 index 000000000..2b58791c3 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/system_hooks.json @@ -0,0 +1 @@ +[{"id": 3, "url": "http://example.com/hook", "created_at": "2013-10-02T10:15:31Z"}] diff --git a/lib/gitlab-cli/spec/fixtures/tag.json b/lib/gitlab-cli/spec/fixtures/tag.json new file mode 100644 index 000000000..a56a09262 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/tag.json @@ -0,0 +1 @@ +{"name": "v1.0.0","commit": {"id": "2695effb5807a22ff3d138d593fd856244e155e7","parents": [],"message": "Initial commit","authored_date": "2012-05-28T04:42:42-07:00","author_name": "John Smith","author email": "john@example.com","committer_name": "Jack Smith","committed_date": "2012-05-28T04:42:42-07:00","committer_email": "jack@example.com"},"protected": false} diff --git a/lib/gitlab-cli/spec/fixtures/team_member.json b/lib/gitlab-cli/spec/fixtures/team_member.json new file mode 100644 index 000000000..fd3ac3852 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/team_member.json @@ -0,0 +1 @@ +{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z","access_level":40} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/team_members.json b/lib/gitlab-cli/spec/fixtures/team_members.json new file mode 100644 index 000000000..a2fe19e3b --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/team_members.json @@ -0,0 +1 @@ +[{"id":1,"email":"john@example.com","name":"John Smith","blocked":false,"created_at":"2012-09-17T09:41:56Z","access_level":40},{"id":2,"email":"jack@example.com","name":"Jack Smith","blocked":false,"created_at":"2012-09-17T09:42:03Z","access_level":20},{"id":3,"email":"wilma@mayerblanda.ca","name":"Beatrice Jewess","blocked":false,"created_at":"2012-09-17T09:42:03Z","access_level":40},{"id":5,"email":"aliza_stark@schmeler.info","name":"Michale Von","blocked":false,"created_at":"2012-09-17T09:42:03Z","access_level":40},{"id":6,"email":"faye.watsica@rohanwalter.com","name":"Ambrose Hansen","blocked":false,"created_at":"2012-09-17T09:42:03Z","access_level":40},{"id":7,"email":"maida@walshtorp.name","name":"Alana Hahn","blocked":false,"created_at":"2012-09-17T09:42:03Z","access_level":20}] \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/unprotect_branch.json b/lib/gitlab-cli/spec/fixtures/unprotect_branch.json new file mode 100644 index 000000000..854c8274a --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/unprotect_branch.json @@ -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":false} diff --git a/lib/gitlab-cli/spec/fixtures/update_merge_request.json b/lib/gitlab-cli/spec/fixtures/update_merge_request.json new file mode 100644 index 000000000..735819ff3 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/update_merge_request.json @@ -0,0 +1 @@ +{"id":1,"target_branch":"master","source_branch":"api","project_id":3,"title":"A different 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"}} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/user.json b/lib/gitlab-cli/spec/fixtures/user.json new file mode 100644 index 000000000..4e0daca50 --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/user.json @@ -0,0 +1 @@ +{"id":1,"email":"john@example.com","name":"John Smith","bio":null,"skype":"","linkedin":"","twitter":"john","dark_scheme":false,"theme_id":1,"blocked":false,"created_at":"2012-09-17T09:41:56Z"} \ No newline at end of file diff --git a/lib/gitlab-cli/spec/fixtures/users.json b/lib/gitlab-cli/spec/fixtures/users.json new file mode 100644 index 000000000..14c6388bf --- /dev/null +++ b/lib/gitlab-cli/spec/fixtures/users.json @@ -0,0 +1 @@ +[{"id":1,"email":"john@example.com","name":"John Smith","bio":null,"skype":"","linkedin":"","twitter":"john","dark_scheme":false,"theme_id":1,"blocked":false,"created_at":"2012-09-17T09:41:56Z"},{"id":2,"email":"jack@example.com","name":"Jack Smith","bio":null,"skype":"","linkedin":"","twitter":"","dark_scheme":false,"theme_id":1,"blocked":false,"created_at":"2012-09-17T09:42:03Z"},{"id":3,"email":"wilma@mayerblanda.ca","name":"Beatrice Jewess","bio":null,"skype":"","linkedin":"","twitter":"","dark_scheme":false,"theme_id":1,"blocked":false,"created_at":"2012-09-17T09:42:03Z"},{"id":4,"email":"nicole@mertz.com","name":"Felipe Davis","bio":null,"skype":"","linkedin":"","twitter":"","dark_scheme":false,"theme_id":1,"blocked":false,"created_at":"2012-09-17T09:42:03Z"},{"id":5,"email":"aliza_stark@schmeler.info","name":"Michale Von","bio":null,"skype":"","linkedin":"","twitter":"","dark_scheme":false,"theme_id":1,"blocked":false,"created_at":"2012-09-17T09:42:03Z"},{"id":6,"email":"faye.watsica@rohanwalter.com","name":"Ambrose Hansen","bio":null,"skype":"","linkedin":"","twitter":"","dark_scheme":false,"theme_id":1,"blocked":false,"created_at":"2012-09-17T09:42:03Z"},{"id":7,"email":"maida@walshtorp.name","name":"Alana Hahn","bio":null,"skype":"","linkedin":"","twitter":"","dark_scheme":false,"theme_id":1,"blocked":false,"created_at":"2012-09-17T09:42:03Z"}] \ No newline at end of file diff --git a/lib/gitlab-cli/spec/gitlab/cli_spec.rb b/lib/gitlab-cli/spec/gitlab/cli_spec.rb new file mode 100644 index 000000000..8b002ec64 --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/cli_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe Gitlab::CLI do + describe ".run" do + context "when command is version" do + it "should show gem version" do + output = capture_output { Gitlab::CLI.run('-v') } + expect(output).to eq("Gitlab Ruby Gem #{Gitlab::VERSION}\n") + end + end + + context "when command is info" do + it "should show environment info" do + output = capture_output { Gitlab::CLI.run('info') } + expect(output).to include("Gitlab endpoint is") + expect(output).to include("Gitlab private token is") + expect(output).to include("Ruby Version is") + expect(output).to include("Gitlab Ruby Gem") + end + end + + context "when command is help" do + it "should show available actions" do + output = capture_output { Gitlab::CLI.run('help') } + expect(output).to include('Available commands') + expect(output).to include('MergeRequests') + expect(output).to include('team_members') + end + end + + context "when command is user" do + before do + stub_get("/user", "user") + @output = capture_output { Gitlab::CLI.run('user') } + end + + it "should show executed command" do + expect(@output).to include('Gitlab.user') + end + + it "should show user data" do + expect(@output).to include('name') + expect(@output).to include('John Smith') + end + end + end + + describe ".start" do + context "when command with excluded fields" do + before do + stub_get("/user", "user") + args = ['user', '--except=id,email,name'] + @output = capture_output { Gitlab::CLI.start(args) } + end + + it "should show user data with excluded fields" do + expect(@output).to_not include('John Smith') + expect(@output).to include('bio') + expect(@output).to include('created_at') + end + end + + context "when command with required fields" do + before do + stub_get("/user", "user") + args = ['user', '--only=id,email,name'] + @output = capture_output { Gitlab::CLI.start(args) } + end + + it "should show user data with required fields" do + expect(@output).to include('id') + expect(@output).to include('name') + expect(@output).to include('email') + expect(@output).to include('John Smith') + expect(@output).to_not include('bio') + expect(@output).to_not include('created_at') + end + end + end +end diff --git a/lib/gitlab-cli/spec/gitlab/client/branches_spec.rb b/lib/gitlab-cli/spec/gitlab/client/branches_spec.rb new file mode 100644 index 000000000..80c18ccb5 --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/client/branches_spec.rb @@ -0,0 +1,103 @@ +require 'spec_helper' + +describe Gitlab::Client do + it { should respond_to :repo_branches } + it { should respond_to :repo_branch } + it { should respond_to :repo_protect_branch } + it { should respond_to :repo_unprotect_branch } + + describe ".branches" do + before do + stub_get("/projects/3/repository/branches", "branches") + @branches = Gitlab.branches(3) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/repository/branches")).to have_been_made + end + + it "should return an array of repository branches" do + expect(@branches).to be_an Array + expect(@branches.first.name).to eq("api") + end + end + + describe ".branch" do + before do + stub_get("/projects/3/repository/branches/api", "branch") + @branch = Gitlab.branch(3, "api") + end + + it "should get the correct resource" do + expect(a_get("/projects/3/repository/branches/api")).to have_been_made + end + + it "should return information about a repository branch" do + expect(@branch.name).to eq("api") + end + end + + describe ".protect_branch" do + before do + stub_put("/projects/3/repository/branches/api/protect", "protect_branch") + @branch = Gitlab.protect_branch(3, "api") + end + + it "should get the correct resource" do + expect(a_put("/projects/3/repository/branches/api/protect")).to have_been_made + end + + it "should return information about a protected repository branch" do + expect(@branch.name).to eq("api") + expect(@branch.protected).to eq(true) + end + end + + describe ".unprotect_branch" do + before do + stub_put("/projects/3/repository/branches/api/unprotect", "unprotect_branch") + @branch = Gitlab.unprotect_branch(3, "api") + end + + it "should get the correct resource" do + expect(a_put("/projects/3/repository/branches/api/unprotect")).to have_been_made + end + + it "should return information about an unprotected repository branch" do + expect(@branch.name).to eq("api") + expect(@branch.protected).to eq(false) + end + end + + describe ".create_branch" do + context "with branch name" do + before do + stub_post("/projects/3/repository/branches", "create_branch") + @branch = Gitlab.create_branch(3, "api","master") + end + + it "should get the correct resource" do + expect(a_post("/projects/3/repository/branches")).to have_been_made + end + + it "should return information about a new repository branch" do + expect(@branch.name).to eq("api") + end + end + context "with commit hash" do + before do + stub_post("/projects/3/repository/branches", "create_branch") + @branch = Gitlab.create_branch(3, "api","949b1df930bedace1dbd755aaa4a82e8c451a616") + end + + it "should get the correct resource" do + expect(a_post("/projects/3/repository/branches")).to have_been_made + end + + it "should return information about a new repository branch" do + expect(@branch.name).to eq("api") + end + end + end + +end diff --git a/lib/gitlab-cli/spec/gitlab/client/groups_spec.rb b/lib/gitlab-cli/spec/gitlab/client/groups_spec.rb new file mode 100644 index 000000000..ad17aedaa --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/client/groups_spec.rb @@ -0,0 +1,111 @@ +require 'spec_helper' + +describe Gitlab::Client do + describe ".groups" do + before do + stub_get("/groups", "groups") + stub_get("/groups/3", "group") + @group = Gitlab.group(3) + @groups = Gitlab.groups + end + + it "should get the correct resource" do + expect(a_get("/groups")).to have_been_made + expect(a_get("/groups/3")).to have_been_made + end + + it "should return an array of Groups" do + expect(@groups).to be_an Array + expect(@groups.first.path).to eq("threegroup") + end + end + + describe ".create_group" do + before do + stub_post("/groups", "group_create") + @group = Gitlab.create_group('GitLab-Group', 'gitlab-path') + end + + it "should get the correct resource" do + expect(a_post("/groups"). + with(:body => {:path => 'gitlab-path', :name => 'GitLab-Group'})).to have_been_made + end + + it "should return information about a created group" do + expect(@group.name).to eq("Gitlab-Group") + expect(@group.path).to eq("gitlab-group") + end + end + + describe ".transfer_project_to_group" do + before do + stub_post("/projects", "project") + @project = Gitlab.create_project('Gitlab') + stub_post("/groups", "group_create") + @group = Gitlab.create_group('GitLab-Group', 'gitlab-path') + + stub_post("/groups/#{@group.id}/projects/#{@project.id}", "group_create") + @group_transfer = Gitlab.transfer_project_to_group(@group.id,@project.id) + end + + it "should post to the correct resource" do + expect(a_post("/groups/#{@group.id}/projects/#{@project.id}").with(:body => {:id => @group.id.to_s, :project_id => @project.id.to_s})).to have_been_made + end + + it "should return information about the group" do + expect(@group_transfer.name).to eq(@group.name) + expect(@group_transfer.path).to eq(@group.path) + expect(@group_transfer.id).to eq(@group.id) + end + end + + describe ".group_members" do + before do + stub_get("/groups/3/members", "group_members") + @members = Gitlab.group_members(3) + end + + it "should get the correct resource" do + expect(a_get("/groups/3/members")).to have_been_made + end + + it "should return information about a group members" do + expect(@members).to be_an Array + expect(@members.size).to eq(2) + expect(@members[1].name).to eq("John Smith") + end + end + + describe ".add_group_member" do + before do + stub_post("/groups/3/members", "group_member") + @member = Gitlab.add_group_member(3, 1, 40) + end + + it "should get the correct resource" do + expect(a_post("/groups/3/members"). + with(:body => {:user_id => '1', :access_level => '40'})).to have_been_made + end + + it "should return information about an added member" do + expect(@member.name).to eq("John Smith") + end + end + + describe ".remove_group_member" do + before do + stub_delete("/groups/3/members/1", "group_member_delete") + @group = Gitlab.remove_group_member(3, 1) + end + + it "should get the correct resource" do + expect(a_delete("/groups/3/members/1")).to have_been_made + end + + it "should return information about the group the member was removed from" do + expect(@group.group_id).to eq(3) + end + end + + +end diff --git a/lib/gitlab-cli/spec/gitlab/client/issues_spec.rb b/lib/gitlab-cli/spec/gitlab/client/issues_spec.rb new file mode 100644 index 000000000..e5a506b94 --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/client/issues_spec.rb @@ -0,0 +1,122 @@ +require 'spec_helper' + +describe Gitlab::Client do + describe ".issues" do + context "with project ID passed" do + before do + stub_get("/projects/3/issues", "project_issues") + @issues = Gitlab.issues(3) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/issues")).to have_been_made + end + + it "should return an array of project's issues" do + expect(@issues).to be_an Array + expect(@issues.first.project_id).to eq(3) + end + end + + context "without project ID passed" do + before do + stub_get("/issues", "issues") + @issues = Gitlab.issues + end + + it "should get the correct resource" do + expect(a_get("/issues")).to have_been_made + end + + it "should return an array of user's issues" do + expect(@issues).to be_an Array + expect(@issues.first.closed).to be_falsey + expect(@issues.first.author.name).to eq("John Smith") + end + end + end + + describe ".issue" do + before do + stub_get("/projects/3/issues/33", "issue") + @issue = Gitlab.issue(3, 33) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/issues/33")).to have_been_made + end + + it "should return information about an issue" do + expect(@issue.project_id).to eq(3) + expect(@issue.assignee.name).to eq("Jack Smith") + end + end + + describe ".create_issue" do + before do + stub_post("/projects/3/issues", "issue") + @issue = Gitlab.create_issue(3, 'title') + end + + it "should get the correct resource" do + expect(a_post("/projects/3/issues"). + with(:body => {:title => 'title'})).to have_been_made + end + + it "should return information about a created issue" do + expect(@issue.project_id).to eq(3) + expect(@issue.assignee.name).to eq("Jack Smith") + end + end + + describe ".edit_issue" do + before do + stub_put("/projects/3/issues/33", "issue") + @issue = Gitlab.edit_issue(3, 33, :title => 'title') + end + + it "should get the correct resource" do + expect(a_put("/projects/3/issues/33"). + with(:body => {:title => 'title'})).to have_been_made + end + + it "should return information about an edited issue" do + expect(@issue.project_id).to eq(3) + expect(@issue.assignee.name).to eq("Jack Smith") + end + end + + describe ".close_issue" do + before do + stub_put("/projects/3/issues/33", "issue") + @issue = Gitlab.close_issue(3, 33) + end + + it "should get the correct resource" do + expect(a_put("/projects/3/issues/33"). + with(:body => {:state_event => 'close'})).to have_been_made + end + + it "should return information about an closed issue" do + expect(@issue.project_id).to eq(3) + expect(@issue.assignee.name).to eq("Jack Smith") + end + end + + describe ".reopen_issue" do + before do + stub_put("/projects/3/issues/33", "issue") + @issue = Gitlab.reopen_issue(3, 33) + end + + it "should get the correct resource" do + expect(a_put("/projects/3/issues/33"). + with(:body => {:state_event => 'reopen'})).to have_been_made + end + + it "should return information about an reopened issue" do + expect(@issue.project_id).to eq(3) + expect(@issue.assignee.name).to eq("Jack Smith") + end + end +end diff --git a/lib/gitlab-cli/spec/gitlab/client/merge_requests_spec.rb b/lib/gitlab-cli/spec/gitlab/client/merge_requests_spec.rb new file mode 100644 index 000000000..a336da9f5 --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/client/merge_requests_spec.rb @@ -0,0 +1,124 @@ +require 'spec_helper' + +describe Gitlab::Client do + describe ".merge_requests" do + before do + stub_get("/projects/3/merge_requests", "merge_requests") + @merge_requests = Gitlab.merge_requests(3) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/merge_requests")).to have_been_made + end + + it "should return an array of project's merge requests" do + expect(@merge_requests).to be_an Array + expect(@merge_requests.first.project_id).to eq(3) + end + end + + describe ".merge_request" do + before do + stub_get("/projects/3/merge_request/1", "merge_request") + @merge_request = Gitlab.merge_request(3, 1) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/merge_request/1")).to have_been_made + end + + it "should return information about a merge request" do + expect(@merge_request.project_id).to eq(3) + expect(@merge_request.assignee.name).to eq("Jack Smith") + end + end + + describe ".create_merge_request" do + before do + stub_post("/projects/3/merge_requests", "create_merge_request") + end + + it "should fail if it doesn't have a source_branch" do + expect { + Gitlab.create_merge_request(3, 'New merge request', :target_branch => 'master') + }.to raise_error Gitlab::Error::MissingAttributes + end + + it "should fail if it doesn't have a target_branch" do + expect { + Gitlab.create_merge_request(3, 'New merge request', :source_branch => 'dev') + }.to raise_error Gitlab::Error::MissingAttributes + end + + it "should return information about a merge request" do + @merge_request = Gitlab.create_merge_request(3, 'New feature', + :source_branch => 'api', + :target_branch => 'master' + ) + expect(@merge_request.project_id).to eq(3) + expect(@merge_request.assignee.name).to eq("Jack Smith") + expect(@merge_request.title).to eq('New feature') + end + end + + describe ".update_merge_request" do + before do + stub_put("/projects/3/merge_request/2", "update_merge_request") + @merge_request = Gitlab.update_merge_request(3, 2, + :assignee_id => '1', + :target_branch => 'master', + :title => 'A different new feature' + ) + end + + it "should return information about a merge request" do + expect(@merge_request.project_id).to eq(3) + expect(@merge_request.assignee.name).to eq("Jack Smith") + expect(@merge_request.title).to eq('A different new feature') + end + end + + describe ".merge_request_comments" do + before do + stub_get("/projects/3/merge_request/2/comments", "merge_request_comments") + @merge_request = Gitlab.merge_request_comments(3, 2) + end + + it "should return merge request's comments" do + expect(@merge_request).to be_an Array + expect(@merge_request.length).to eq(2) + expect(@merge_request[0].note).to eq("this is the 1st comment on the 2merge merge request") + expect(@merge_request[0].author.id).to eq(11) + expect(@merge_request[1].note).to eq("another discussion point on the 2merge request") + expect(@merge_request[1].author.id).to eq(12) + end + end + + describe ".merge_request_comments" do + before do + stub_get("/projects/3/merge_request/2/comments", "merge_request_comments") + @merge_request = Gitlab.merge_request_comments(3, 2) + end + + it "should return merge request's comments" do + expect(@merge_request).to be_an Array + expect(@merge_request.length).to eq(2) + expect(@merge_request[0].note).to eq("this is the 1st comment on the 2merge merge request") + expect(@merge_request[0].author.id).to eq(11) + expect(@merge_request[1].note).to eq("another discussion point on the 2merge request") + expect(@merge_request[1].author.id).to eq(12) + end + end + + describe ".create_merge_request_comment" do + before do + stub_post("/projects/3/merge_request/2/comments", "comment_merge_request") + end + + it "should return information about a merge request" do + @merge_request = Gitlab.create_merge_request_comment(3, 2, 'Cool Merge Request!') + expect(@merge_request.note).to eq('Cool Merge Request!') + @merge_request.author.id == 1 + end + end +end diff --git a/lib/gitlab-cli/spec/gitlab/client/milestones_spec.rb b/lib/gitlab-cli/spec/gitlab/client/milestones_spec.rb new file mode 100644 index 000000000..aa1e66b14 --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/client/milestones_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe Gitlab::Client do + describe ".milestones" do + before do + stub_get("/projects/3/milestones", "milestones") + @milestones = Gitlab.milestones(3) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/milestones")).to have_been_made + end + + it "should return an array of project's milestones" do + expect(@milestones).to be_an Array + expect(@milestones.first.project_id).to eq(3) + end + end + + describe ".milestone" do + before do + stub_get("/projects/3/milestones/1", "milestone") + @milestone = Gitlab.milestone(3, 1) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/milestones/1")).to have_been_made + end + + it "should return information about a milestone" do + expect(@milestone.project_id).to eq(3) + end + end + + describe ".create_milestone" do + before do + stub_post("/projects/3/milestones", "milestone") + @milestone = Gitlab.create_milestone(3, 'title') + end + + it "should get the correct resource" do + expect(a_post("/projects/3/milestones"). + with(:body => {:title => 'title'})).to have_been_made + end + + it "should return information about a created milestone" do + expect(@milestone.project_id).to eq(3) + end + end + + describe ".edit_milestone" do + before do + stub_put("/projects/3/milestones/33", "milestone") + @milestone = Gitlab.edit_milestone(3, 33, :title => 'title') + end + + it "should get the correct resource" do + expect(a_put("/projects/3/milestones/33"). + with(:body => {:title => 'title'})).to have_been_made + end + + it "should return information about an edited milestone" do + expect(@milestone.project_id).to eq(3) + end + end +end diff --git a/lib/gitlab-cli/spec/gitlab/client/notes_spec.rb b/lib/gitlab-cli/spec/gitlab/client/notes_spec.rb new file mode 100644 index 000000000..d80cad476 --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/client/notes_spec.rb @@ -0,0 +1,156 @@ +require 'spec_helper' + +describe Gitlab::Client do + describe "notes" do + context "when wall notes" do + before do + stub_get("/projects/3/notes", "notes") + @notes = Gitlab.notes(3) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/notes")).to have_been_made + end + + it "should return an array of notes" do + expect(@notes).to be_an Array + expect(@notes.first.author.name).to eq("John Smith") + end + end + + context "when issue notes" do + before do + stub_get("/projects/3/issues/7/notes", "notes") + @notes = Gitlab.issue_notes(3, 7) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/issues/7/notes")).to have_been_made + end + + it "should return an array of notes" do + expect(@notes).to be_an Array + expect(@notes.first.author.name).to eq("John Smith") + end + end + + context "when snippet notes" do + before do + stub_get("/projects/3/snippets/7/notes", "notes") + @notes = Gitlab.snippet_notes(3, 7) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/snippets/7/notes")).to have_been_made + end + + it "should return an array of notes" do + expect(@notes).to be_an Array + expect(@notes.first.author.name).to eq("John Smith") + end + end + end + + describe "note" do + context "when wall note" do + before do + stub_get("/projects/3/notes/1201", "note") + @note = Gitlab.note(3, 1201) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/notes/1201")).to have_been_made + end + + it "should return information about a note" do + expect(@note.body).to eq("The solution is rather tricky") + expect(@note.author.name).to eq("John Smith") + end + end + + context "when issue note" do + before do + stub_get("/projects/3/issues/7/notes/1201", "note") + @note = Gitlab.issue_note(3, 7, 1201) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/issues/7/notes/1201")).to have_been_made + end + + it "should return information about a note" do + expect(@note.body).to eq("The solution is rather tricky") + expect(@note.author.name).to eq("John Smith") + end + end + + context "when snippet note" do + before do + stub_get("/projects/3/snippets/7/notes/1201", "note") + @note = Gitlab.snippet_note(3, 7, 1201) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/snippets/7/notes/1201")).to have_been_made + end + + it "should return information about a note" do + expect(@note.body).to eq("The solution is rather tricky") + expect(@note.author.name).to eq("John Smith") + end + end + end + + describe "create note" do + context "when wall note" do + before do + stub_post("/projects/3/notes", "note") + @note = Gitlab.create_note(3, "The solution is rather tricky") + end + + it "should get the correct resource" do + expect(a_post("/projects/3/notes"). + with(:body => {:body => 'The solution is rather tricky'})).to have_been_made + end + + it "should return information about a created note" do + expect(@note.body).to eq("The solution is rather tricky") + expect(@note.author.name).to eq("John Smith") + end + end + + context "when issue note" do + before do + stub_post("/projects/3/issues/7/notes", "note") + @note = Gitlab.create_issue_note(3, 7, "The solution is rather tricky") + end + + it "should get the correct resource" do + expect(a_post("/projects/3/issues/7/notes"). + with(:body => {:body => 'The solution is rather tricky'})).to have_been_made + end + + it "should return information about a created note" do + expect(@note.body).to eq("The solution is rather tricky") + expect(@note.author.name).to eq("John Smith") + end + end + + context "when snippet note" do + before do + stub_post("/projects/3/snippets/7/notes", "note") + @note = Gitlab.create_snippet_note(3, 7, "The solution is rather tricky") + end + + it "should get the correct resource" do + expect(a_post("/projects/3/snippets/7/notes"). + with(:body => {:body => 'The solution is rather tricky'})).to have_been_made + end + + it "should return information about a created note" do + expect(@note.body).to eq("The solution is rather tricky") + expect(@note.author.name).to eq("John Smith") + end + end + end +end diff --git a/lib/gitlab-cli/spec/gitlab/client/projects_spec.rb b/lib/gitlab-cli/spec/gitlab/client/projects_spec.rb new file mode 100644 index 000000000..5096f8f66 --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/client/projects_spec.rb @@ -0,0 +1,357 @@ +require 'spec_helper' + +describe Gitlab::Client do + describe ".projects" do + before do + stub_get("/projects", "projects") + @projects = Gitlab.projects + end + + it "should get the correct resource" do + expect(a_get("/projects")).to have_been_made + end + + it "should return an array of projects" do + expect(@projects).to be_an Array + expect(@projects.first.name).to eq("Brute") + expect(@projects.first.owner.name).to eq("John Smith") + end + end + + describe ".project" do + before do + stub_get("/projects/3", "project") + @project = Gitlab.project(3) + end + + it "should get the correct resource" do + expect(a_get("/projects/3")).to have_been_made + end + + it "should return information about a project" do + expect(@project.name).to eq("Gitlab") + expect(@project.owner.name).to eq("John Smith") + end + end + + describe ".project_events" do + before do + stub_get("/projects/2/events", "project_events") + @events = Gitlab.project_events(2) + end + + it "should get the correct resource" do + expect(a_get("/projects/2/events")).to have_been_made + end + + it "should return an array of events" do + expect(@events).to be_an Array + expect(@events.size).to eq(2) + end + + it "should return the action name of the event" do + expect(@events.first.action_name).to eq("opened") + end + end + + describe ".create_project" do + before do + stub_post("/projects", "project") + @project = Gitlab.create_project('Gitlab') + end + + it "should get the correct resource" do + expect(a_post("/projects")).to have_been_made + end + + it "should return information about a created project" do + expect(@project.name).to eq("Gitlab") + expect(@project.owner.name).to eq("John Smith") + end + end + + describe ".create_project for user" do + before do + stub_post("/users", "user") + @owner = Gitlab.create_user("john@example.com", "pass", {name: 'John Owner'}) + stub_post("/projects/user/#{@owner.id}", "project_for_user") + @project = Gitlab.create_project('Brute', {:user_id => @owner.id}) + end + + it "should return information about a created project" do + expect(@project.name).to eq("Brute") + expect(@project.owner.name).to eq("John Owner") + end + end + + describe ".delete_project" do + before do + stub_delete("/projects/Gitlab", "project") + @project = Gitlab.delete_project('Gitlab') + end + + it "should get the correct resource" do + expect(a_delete("/projects/Gitlab")).to have_been_made + end + + it "should return information about a deleted project" do + expect(@project.name).to eq("Gitlab") + expect(@project.owner.name).to eq("John Smith") + end + end + + describe ".team_members" do + before do + stub_get("/projects/3/members", "team_members") + @team_members = Gitlab.team_members(3) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/members")).to have_been_made + end + + it "should return an array of team members" do + expect(@team_members).to be_an Array + expect(@team_members.first.name).to eq("John Smith") + end + end + + describe ".team_member" do + before do + stub_get("/projects/3/members/1", "team_member") + @team_member = Gitlab.team_member(3, 1) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/members/1")).to have_been_made + end + + it "should return information about a team member" do + expect(@team_member.name).to eq("John Smith") + end + end + + describe ".add_team_member" do + before do + stub_post("/projects/3/members", "team_member") + @team_member = Gitlab.add_team_member(3, 1, 40) + end + + it "should get the correct resource" do + expect(a_post("/projects/3/members"). + with(:body => {:user_id => '1', :access_level => '40'})).to have_been_made + end + + it "should return information about an added team member" do + expect(@team_member.name).to eq("John Smith") + end + end + + describe ".edit_team_member" do + before do + stub_put("/projects/3/members/1", "team_member") + @team_member = Gitlab.edit_team_member(3, 1, 40) + end + + it "should get the correct resource" do + expect(a_put("/projects/3/members/1"). + with(:body => {:access_level => '40'})).to have_been_made + end + + it "should return information about an edited team member" do + expect(@team_member.name).to eq("John Smith") + end + end + + describe ".remove_team_member" do + before do + stub_delete("/projects/3/members/1", "team_member") + @team_member = Gitlab.remove_team_member(3, 1) + end + + it "should get the correct resource" do + expect(a_delete("/projects/3/members/1")).to have_been_made + end + + it "should return information about a removed team member" do + expect(@team_member.name).to eq("John Smith") + end + end + + describe ".project_hooks" do + before do + stub_get("/projects/1/hooks", "project_hooks") + @hooks = Gitlab.project_hooks(1) + end + + it "should get the correct resource" do + expect(a_get("/projects/1/hooks")).to have_been_made + end + + it "should return an array of hooks" do + expect(@hooks).to be_an Array + expect(@hooks.first.url).to eq("https://api.example.net/v1/webhooks/ci") + end + end + + describe ".project_hook" do + before do + stub_get("/projects/1/hooks/1", "project_hook") + @hook = Gitlab.project_hook(1, 1) + end + + it "should get the correct resource" do + expect(a_get("/projects/1/hooks/1")).to have_been_made + end + + it "should return information about a hook" do + expect(@hook.url).to eq("https://api.example.net/v1/webhooks/ci") + end + end + + describe ".add_project_hook" do + context "without specified events" do + before do + stub_post("/projects/1/hooks", "project_hook") + @hook = Gitlab.add_project_hook(1, "https://api.example.net/v1/webhooks/ci") + end + + it "should get the correct resource" do + body = {:url => "https://api.example.net/v1/webhooks/ci"} + expect(a_post("/projects/1/hooks").with(:body => body)).to have_been_made + end + + it "should return information about an added hook" do + expect(@hook.url).to eq("https://api.example.net/v1/webhooks/ci") + end + end + + context "with specified events" do + before do + stub_post("/projects/1/hooks", "project_hook") + @hook = Gitlab.add_project_hook(1, "https://api.example.net/v1/webhooks/ci", push_events: true, merge_requests_events: true) + end + + it "should get the correct resource" do + body = {:url => "https://api.example.net/v1/webhooks/ci", push_events: "true", merge_requests_events: "true"} + expect(a_post("/projects/1/hooks").with(:body => body)).to have_been_made + end + + it "should return information about an added hook" do + expect(@hook.url).to eq("https://api.example.net/v1/webhooks/ci") + end + end + end + + describe ".edit_project_hook" do + before do + stub_put("/projects/1/hooks/1", "project_hook") + @hook = Gitlab.edit_project_hook(1, 1, "https://api.example.net/v1/webhooks/ci") + end + + it "should get the correct resource" do + body = {:url => "https://api.example.net/v1/webhooks/ci"} + expect(a_put("/projects/1/hooks/1").with(:body => body)).to have_been_made + end + + it "should return information about an edited hook" do + expect(@hook.url).to eq("https://api.example.net/v1/webhooks/ci") + end + end + + describe ".delete_project_hook" do + before do + stub_delete("/projects/1/hooks/1", "project_hook") + @hook = Gitlab.delete_project_hook(1, 1) + end + + it "should get the correct resource" do + expect(a_delete("/projects/1/hooks/1")).to have_been_made + end + + it "should return information about a deleted hook" do + expect(@hook.url).to eq("https://api.example.net/v1/webhooks/ci") + end + end + + describe ".make_forked_from" do + before do + stub_post("/projects/42/fork/24", "project_fork_link") + @forked_project_link = Gitlab.make_forked_from(42, 24) + end + + it "should get the correct resource" do + expect(a_post("/projects/42/fork/24")).to have_been_made + end + + it "should return information about a forked project" do + expect(@forked_project_link.forked_from_project_id).to eq(24) + expect(@forked_project_link.forked_to_project_id).to eq(42) + end + end + + describe ".remove_forked" do + before do + stub_delete("/projects/42/fork", "project_fork_link") + @forked_project_link = Gitlab.remove_forked(42) + end + + it "should be sent to correct resource" do + expect(a_delete("/projects/42/fork")).to have_been_made + end + + it "should return information about an unforked project" do + expect(@forked_project_link.forked_to_project_id).to eq(42) + end + end + + describe ".deploy_keys" do + before do + stub_get("/projects/42/keys", "project_keys") + @deploy_keys = Gitlab.deploy_keys(42) + end + + it "should get the correct resource" do + expect(a_get("/projects/42/keys")).to have_been_made + end + + it "should return project deploy keys" do + expect(@deploy_keys).to be_an Array + expect(@deploy_keys.first.id).to eq 2 + expect(@deploy_keys.first.title).to eq "Key Title" + expect(@deploy_keys.first.key).to match(/ssh-rsa/) + end + end + + describe ".deploy_key" do + before do + stub_get("/projects/42/keys/2", "project_key") + @deploy_key = Gitlab.deploy_key(42, 2) + end + + it "should get the correct resource" do + expect(a_get("/projects/42/keys/2")).to have_been_made + end + + it "should return project deploy key" do + expect(@deploy_key.id).to eq 2 + expect(@deploy_key.title).to eq "Key Title" + expect(@deploy_key.key).to match(/ssh-rsa/) + end + end + + describe ".delete_deploy_key" do + before do + stub_delete("/projects/42/keys/2", "project_delete_key") + @deploy_key = Gitlab.delete_deploy_key(42, 2) + end + + it "should get the correct resource" do + expect(a_delete("/projects/42/keys/2")).to have_been_made + end + + it "should return information about a deleted key" do + expect(@deploy_key.id).to eq(2) + end + end +end diff --git a/lib/gitlab-cli/spec/gitlab/client/repositories_spec.rb b/lib/gitlab-cli/spec/gitlab/client/repositories_spec.rb new file mode 100644 index 000000000..f58e315d0 --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/client/repositories_spec.rb @@ -0,0 +1,92 @@ +require 'spec_helper' + +describe Gitlab::Client do + it { should respond_to :repo_tags } + it { should respond_to :repo_create_tag } + it { should respond_to :repo_branches } + it { should respond_to :repo_branch } + it { should respond_to :repo_commits } + it { should respond_to :repo_commit } + it { should respond_to :repo_commit_diff } + + describe ".tags" do + before do + stub_get("/projects/3/repository/tags", "project_tags") + @tags = Gitlab.tags(3) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/repository/tags")).to have_been_made + end + + it "should return an array of repository tags" do + expect(@tags).to be_an Array + expect(@tags.first.name).to eq("v2.8.2") + end + end + + describe ".create_tag" do + before do + stub_post("/projects/3/repository/tags", "tag") + @tag = Gitlab.create_tag(3, 'v1.0.0', '2695effb5807a22ff3d138d593fd856244e155e7') + end + + it "should get the correct resource" do + expect(a_post("/projects/3/repository/tags")).to have_been_made + end + + it "should return information about a new repository tag" do + expect(@tag.name).to eq("v1.0.0") + end + end + + describe ".commits" do + before do + stub_get("/projects/3/repository/commits", "project_commits"). + with(:query => {:ref_name => "api"}) + @commits = Gitlab.commits(3, :ref_name => "api") + end + + it "should get the correct resource" do + expect(a_get("/projects/3/repository/commits"). + with(:query => {:ref_name => "api"})).to have_been_made + end + + it "should return an array of repository commits" do + expect(@commits).to be_an Array + expect(@commits.first.id).to eq("f7dd067490fe57505f7226c3b54d3127d2f7fd46") + end + end + + describe ".commit" do + before do + stub_get("/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6", "project_commit") + @commit = Gitlab.commit(3, '6104942438c14ec7bd21c6cd5bd995272b3faff6') + end + + it "should get the correct resource" do + expect(a_get("/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6")) + .to have_been_made + end + + it "should return a repository commit" do + expect(@commit.id).to eq("6104942438c14ec7bd21c6cd5bd995272b3faff6") + end + end + + describe ".commit_diff" do + before do + stub_get("/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/diff", "project_commit_diff") + @diff = Gitlab.commit_diff(3, '6104942438c14ec7bd21c6cd5bd995272b3faff6') + end + + it "should get the correct resource" do + expect(a_get("/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/diff")) + .to have_been_made + end + + it "should return a diff of a commit" do + expect(@diff.new_path).to eq("doc/update/5.4-to-6.0.md") + end + end +end diff --git a/lib/gitlab-cli/spec/gitlab/client/snippets_spec.rb b/lib/gitlab-cli/spec/gitlab/client/snippets_spec.rb new file mode 100644 index 000000000..b6ceecc0d --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/client/snippets_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper' + +describe Gitlab::Client do + describe ".snippets" do + before do + stub_get("/projects/3/snippets", "snippets") + @snippets = Gitlab.snippets(3) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/snippets")).to have_been_made + end + + it "should return an array of project's snippets" do + expect(@snippets).to be_an Array + expect(@snippets.first.file_name).to eq("mailer_test.rb") + end + end + + describe ".snippet" do + before do + stub_get("/projects/3/snippets/1", "snippet") + @snippet = Gitlab.snippet(3, 1) + end + + it "should get the correct resource" do + expect(a_get("/projects/3/snippets/1")).to have_been_made + end + + it "should return information about a snippet" do + expect(@snippet.file_name).to eq("mailer_test.rb") + expect(@snippet.author.name).to eq("John Smith") + end + end + + describe ".create_snippet" do + before do + stub_post("/projects/3/snippets", "snippet") + @snippet = Gitlab.create_snippet(3, {:title => 'API', :file_name => 'api.rb', :code => 'code'}) + end + + it "should get the correct resource" do + body = {:title => 'API', :file_name => 'api.rb', :code => 'code'} + expect(a_post("/projects/3/snippets").with(:body => body)).to have_been_made + end + + it "should return information about a new snippet" do + expect(@snippet.file_name).to eq("mailer_test.rb") + expect(@snippet.author.name).to eq("John Smith") + end + end + + describe ".edit_snippet" do + before do + stub_put("/projects/3/snippets/1", "snippet") + @snippet = Gitlab.edit_snippet(3, 1, :file_name => 'mailer_test.rb') + end + + it "should get the correct resource" do + expect(a_put("/projects/3/snippets/1"). + with(:body => {:file_name => 'mailer_test.rb'})).to have_been_made + end + + it "should return information about an edited snippet" do + expect(@snippet.file_name).to eq("mailer_test.rb") + expect(@snippet.author.name).to eq("John Smith") + end + end + + describe ".delete_snippet" do + before do + stub_delete("/projects/3/snippets/1", "snippet") + @snippet = Gitlab.delete_snippet(3, 1) + end + + it "should get the correct resource" do + expect(a_delete("/projects/3/snippets/1")).to have_been_made + end + + it "should return information about a deleted snippet" do + expect(@snippet.file_name).to eq("mailer_test.rb") + expect(@snippet.author.name).to eq("John Smith") + end + end +end diff --git a/lib/gitlab-cli/spec/gitlab/client/system_hooks_spec.rb b/lib/gitlab-cli/spec/gitlab/client/system_hooks_spec.rb new file mode 100644 index 000000000..7410c3655 --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/client/system_hooks_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +describe Gitlab::Client do + it { should respond_to :system_hooks } + it { should respond_to :add_system_hook } + it { should respond_to :system_hook } + it { should respond_to :delete_system_hook } + + describe ".hooks" do + before do + stub_get("/hooks", "system_hooks") + @hooks = Gitlab.hooks + end + + it "should get the correct resource" do + expect(a_get("/hooks")).to have_been_made + end + + it "should return an array of system hooks" do + expect(@hooks).to be_an Array + expect(@hooks.first.url).to eq("http://example.com/hook") + end + end + + describe ".add_hook" do + before do + stub_post("/hooks", "system_hook") + @hook = Gitlab.add_hook("http://example.com/hook") + end + + it "should get the correct resource" do + expect(a_post("/hooks")).to have_been_made + end + + it "should return information about a added system hook" do + expect(@hook.url).to eq("http://example.com/hook") + end + end + + describe ".hook" do + before do + stub_get("/hooks/3", "system_hook_test") + @hook = Gitlab.hook(3) + end + it "should get the correct resource" do + expect(a_get("/hooks/3")).to have_been_made + end + + it "should return information about a added system hook" do + expect(@hook.event_name).to eq("project_create") + expect(@hook.project_id).to eq(1) + end + end + + describe ".delete_hook" do + before do + stub_delete("/hooks/3", "system_hook") + @hook = Gitlab.delete_hook(3) + end + + it "should get the correct resource" do + expect(a_delete("/hooks/3")).to have_been_made + end + + it "should return information about a deleted system hook" do + expect(@hook.url).to eq("http://example.com/hook") + end + end +end diff --git a/lib/gitlab-cli/spec/gitlab/client/users_spec.rb b/lib/gitlab-cli/spec/gitlab/client/users_spec.rb new file mode 100644 index 000000000..ead205b47 --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/client/users_spec.rb @@ -0,0 +1,192 @@ +require 'spec_helper' + +describe Gitlab::Client do + describe ".users" do + before do + stub_get("/users", "users") + @users = Gitlab.users + end + + it "should get the correct resource" do + expect(a_get("/users")).to have_been_made + end + + it "should return an array of users" do + expect(@users).to be_an Array + expect(@users.first.email).to eq("john@example.com") + end + end + + describe ".user" do + context "with user ID passed" do + before do + stub_get("/users/1", "user") + @user = Gitlab.user(1) + end + + it "should get the correct resource" do + expect(a_get("/users/1")).to have_been_made + end + + it "should return information about a user" do + expect(@user.email).to eq("john@example.com") + end + end + + context "without user ID passed" do + before do + stub_get("/user", "user") + @user = Gitlab.user + end + + it "should get the correct resource" do + expect(a_get("/user")).to have_been_made + end + + it "should return information about an authorized user" do + expect(@user.email).to eq("john@example.com") + end + end + end + + describe ".create_user" do + context "when successful request" do + before do + stub_post("/users", "user") + @user = Gitlab.create_user("email", "pass") + end + + it "should get the correct resource" do + body = {:email => "email", :password => "pass", :name => "email"} + expect(a_post("/users").with(:body => body)).to have_been_made + end + + it "should return information about a created user" do + expect(@user.email).to eq("john@example.com") + end + end + + context "when bad request" do + it "should throw an exception" do + stub_post("/users", "error_already_exists", 409) + expect { + Gitlab.create_user("email", "pass") + }.to raise_error(Gitlab::Error::Conflict, "Server responded with code 409, message: 409 Already exists. Request URI: #{Gitlab.endpoint}/users") + end + end + end + + describe ".edit_user" do + before do + @options = { :name => "Roberto" } + stub_put("/users/1", "user").with(:body => @options) + @user = Gitlab.edit_user(1, @options) + end + + it "should get the correct resource" do + expect(a_put("/users/1").with(:body => @options)).to have_been_made + end + end + + describe ".session" do + after do + Gitlab.endpoint = 'https://api.example.com' + Gitlab.private_token = 'secret' + end + + before do + stub_request(:post, "#{Gitlab.endpoint}/session"). + to_return(:body => load_fixture('session'), :status => 200) + @session = Gitlab.session("email", "pass") + end + + context "when endpoint is not set" do + it "should raise Error::MissingCredentials" do + Gitlab.endpoint = nil + expect { + Gitlab.session("email", "pass") + }.to raise_error(Gitlab::Error::MissingCredentials, 'Please set an endpoint to API') + end + end + + context "when private_token is not set" do + it "should not raise Error::MissingCredentials" do + Gitlab.private_token = nil + expect { Gitlab.session("email", "pass") }.to_not raise_error + end + end + + context "when endpoint is set" do + it "should get the correct resource" do + expect(a_request(:post, "#{Gitlab.endpoint}/session")).to have_been_made + end + + it "should return information about a created session" do + expect(@session.email).to eq("john@example.com") + expect(@session.private_token).to eq("qEsq1pt6HJPaNciie3MG") + end + end + end + + describe ".ssh_keys" do + before do + stub_get("/user/keys", "keys") + @keys = Gitlab.ssh_keys + end + + it "should get the correct resource" do + expect(a_get("/user/keys")).to have_been_made + end + + it "should return an array of SSH keys" do + expect(@keys).to be_an Array + expect(@keys.first.title).to eq("narkoz@helium") + end + end + + describe ".ssh_key" do + before do + stub_get("/user/keys/1", "key") + @key = Gitlab.ssh_key(1) + end + + it "should get the correct resource" do + expect(a_get("/user/keys/1")).to have_been_made + end + + it "should return information about an SSH key" do + expect(@key.title).to eq("narkoz@helium") + end + end + + describe ".create_ssh_key" do + before do + stub_post("/user/keys", "key") + @key = Gitlab.create_ssh_key("title", "body") + end + + it "should get the correct resource" do + body = {:title => "title", :key => "body"} + expect(a_post("/user/keys").with(:body => body)).to have_been_made + end + + it "should return information about a created SSH key" do + expect(@key.title).to eq("narkoz@helium") + end + end + + describe ".delete_ssh_key" do + before do + stub_delete("/user/keys/1", "key") + @key = Gitlab.delete_ssh_key(1) + end + + it "should get the correct resource" do + expect(a_delete("/user/keys/1")).to have_been_made + end + + it "should return information about a deleted SSH key" do + expect(@key.title).to eq("narkoz@helium") + end + end +end diff --git a/lib/gitlab-cli/spec/gitlab/objectified_hash_spec.rb b/lib/gitlab-cli/spec/gitlab/objectified_hash_spec.rb new file mode 100644 index 000000000..db45711df --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/objectified_hash_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe Gitlab::ObjectifiedHash do + before do + @hash = {a: 1, b: 2} + @oh = Gitlab::ObjectifiedHash.new @hash + end + + it "should objectify hash" do + expect(@oh.a).to eq(@hash[:a]) + expect(@oh.b).to eq(@hash[:b]) + end + + describe "#to_hash" do + it "should return an original hash" do + expect(@oh.to_hash).to eq(@hash) + end + + it "should have an alias #to_h" do + expect(@oh.respond_to?(:to_h)).to be_truthy + end + end +end diff --git a/lib/gitlab-cli/spec/gitlab/request_spec.rb b/lib/gitlab-cli/spec/gitlab/request_spec.rb new file mode 100644 index 000000000..869c2e964 --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab/request_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe Gitlab::Request do + it { should respond_to :get } + it { should respond_to :post } + it { should respond_to :put } + it { should respond_to :delete } + + describe ".default_options" do + it "should have default values" do + default_options = Gitlab::Request.default_options + expect(default_options).to be_a Hash + expect(default_options[:parser]).to be_a Proc + expect(default_options[:format]).to eq(:json) + expect(default_options[:headers]).to eq({'Accept' => 'application/json'}) + expect(default_options[:default_params]).to be_nil + end + end + + describe ".parse" do + it "should return ObjectifiedHash" do + body = JSON.unparse({a: 1, b: 2}) + expect(Gitlab::Request.parse(body)).to be_an Gitlab::ObjectifiedHash + end + end + + describe "#set_request_defaults" do + context "when endpoint is not set" do + it "should raise Error::MissingCredentials" do + expect { + Gitlab::Request.new.set_request_defaults(nil, 1234000) + }.to raise_error(Gitlab::Error::MissingCredentials, 'Please set an endpoint to API') + end + end + + context "when endpoint is set" do + it "should set base_uri" do + Gitlab::Request.new.set_request_defaults('http://rabbit-hole.example.org', 1234000) + expect(Gitlab::Request.base_uri).to eq("http://rabbit-hole.example.org") + end + + it "should set default_params" do + Gitlab::Request.new.set_request_defaults('http://rabbit-hole.example.org', 1234000, 'sudoer') + expect(Gitlab::Request.default_params).to eq({:sudo => 'sudoer'}) + end + end + end +end diff --git a/lib/gitlab-cli/spec/gitlab_spec.rb b/lib/gitlab-cli/spec/gitlab_spec.rb new file mode 100644 index 000000000..f037e70aa --- /dev/null +++ b/lib/gitlab-cli/spec/gitlab_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe Gitlab do + after { Gitlab.reset } + + describe ".client" do + it "should be a Gitlab::Client" do + expect(Gitlab.client).to be_a Gitlab::Client + end + end + + describe ".actions" do + it "should return an array of client methods" do + actions = Gitlab.actions + expect(actions).to be_an Array + expect(actions.first).to be_a Symbol + expect(actions.sort.first).to match(/add_/) + end + end + + describe ".endpoint=" do + it "should set endpoint" do + Gitlab.endpoint = 'https://api.example.com' + expect(Gitlab.endpoint).to eq('https://api.example.com') + end + end + + describe ".private_token=" do + it "should set private_token" do + Gitlab.private_token = 'secret' + expect(Gitlab.private_token).to eq('secret') + end + end + + describe ".sudo=" do + it "should set sudo" do + Gitlab.sudo = 'user' + expect(Gitlab.sudo).to eq('user') + end + end + + describe ".user_agent" do + it "should return default user_agent" do + expect(Gitlab.user_agent).to eq(Gitlab::Configuration::DEFAULT_USER_AGENT) + end + end + + describe ".user_agent=" do + it "should set user_agent" do + Gitlab.user_agent = 'Custom User Agent' + expect(Gitlab.user_agent).to eq('Custom User Agent') + end + end + + describe ".configure" do + Gitlab::Configuration::VALID_OPTIONS_KEYS.each do |key| + it "should set #{key}" do + Gitlab.configure do |config| + config.send("#{key}=", key) + expect(Gitlab.send(key)).to eq(key) + end + end + end + end +end diff --git a/lib/gitlab-cli/spec/spec_helper.rb b/lib/gitlab-cli/spec/spec_helper.rb new file mode 100644 index 000000000..28df28be2 --- /dev/null +++ b/lib/gitlab-cli/spec/spec_helper.rb @@ -0,0 +1,74 @@ +require 'rspec' +require 'webmock/rspec' + +require File.expand_path('../../lib/gitlab', __FILE__) +require File.expand_path('../../lib/gitlab/cli', __FILE__) + +def capture_output + out = StringIO.new + $stdout = out + $stderr = out + yield + $stdout = STDOUT + $stderr = STDERR + out.string +end + +def load_fixture(name) + File.new(File.dirname(__FILE__) + "/fixtures/#{name}.json") +end + +RSpec.configure do |config| + config.before(:all) do + Gitlab.endpoint = 'https://api.example.com' + Gitlab.private_token = 'secret' + end +end + +# GET +def stub_get(path, fixture) + stub_request(:get, "#{Gitlab.endpoint}#{path}"). + with(:headers => {'PRIVATE-TOKEN' => Gitlab.private_token}). + to_return(:body => load_fixture(fixture)) +end + +def a_get(path) + a_request(:get, "#{Gitlab.endpoint}#{path}"). + with(:headers => {'PRIVATE-TOKEN' => Gitlab.private_token}) +end + +# POST +def stub_post(path, fixture, status_code=200) + stub_request(:post, "#{Gitlab.endpoint}#{path}"). + with(:headers => {'PRIVATE-TOKEN' => Gitlab.private_token}). + to_return(:body => load_fixture(fixture), :status => status_code) +end + +def a_post(path) + a_request(:post, "#{Gitlab.endpoint}#{path}"). + with(:headers => {'PRIVATE-TOKEN' => Gitlab.private_token}) +end + +# PUT +def stub_put(path, fixture) + stub_request(:put, "#{Gitlab.endpoint}#{path}"). + with(:headers => {'PRIVATE-TOKEN' => Gitlab.private_token}). + to_return(:body => load_fixture(fixture)) +end + +def a_put(path) + a_request(:put, "#{Gitlab.endpoint}#{path}"). + with(:headers => {'PRIVATE-TOKEN' => Gitlab.private_token}) +end + +# DELETE +def stub_delete(path, fixture) + stub_request(:delete, "#{Gitlab.endpoint}#{path}"). + with(:headers => {'PRIVATE-TOKEN' => Gitlab.private_token}). + to_return(:body => load_fixture(fixture)) +end + +def a_delete(path) + a_request(:delete, "#{Gitlab.endpoint}#{path}"). + with(:headers => {'PRIVATE-TOKEN' => Gitlab.private_token}) +end diff --git a/lib/redmine/scm/adapters/gitlab_adapter.rb b/lib/redmine/scm/adapters/gitlab_adapter.rb new file mode 100644 index 000000000..32429fe35 --- /dev/null +++ b/lib/redmine/scm/adapters/gitlab_adapter.rb @@ -0,0 +1,289 @@ +#coding=utf-8 +# +require 'redmine/scm/adapters/abstract_adapter' +require 'base64' + +module Redmine + module Scm + module Adapters + + class GitlabAdapter < AbstractAdapter + + class GitBranch < Branch + attr_accessor :is_default + end + + def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil) + super + @g = Gitlab.client + @project = Repository.find_by_url(url).project.gpid + @path_encoding = path_encoding.blank? ? 'UTF-8' : path_encoding + end + + def path_encoding + @path_encoding + end + + def info + begin + Info.new(:root_url => url, :lastrev => lastrev('',nil)) + rescue + nil + end + end + + def branches + return @branches if @branches + @branches = [] + branches = @g.branches(@project) + branches.each do |line| + name = line.name + scmid = line.commit.id + bran = GitBranch.new(name) + bran.revision = scmid + bran.scmid = name + bran.is_default = true #TODO + @branches << bran + end + @branches.sort! + rescue ScmCommandAborted + nil + end + + def tags + return @tags if @tags + tags = @g.tags(@project) + @tags = [] + tags.each do |tag| + @tags << tag.name + end + rescue ScmCommandAborted + nil + end + + def default_branch + bras = self.branches + return nil if bras.nil? + default_bras = bras.select{|x| x.is_default == true} + return default_bras.first.to_s if ! default_bras.empty? + master_bras = bras.select{|x| x.to_s == 'master'} + master_bras.empty? ? bras.first.to_s : 'master' + end + + def entry(path=nil, identifier=nil) + parts = path.to_s.split(%r{[\/\\]}).select {|n| !n.blank?} + search_path = parts[0..-2].join('/') + search_name = parts[-1] + if search_path.blank? && search_name.blank? + # Root entry + Entry.new(:path => '', :kind => 'dir') + else + # Search for the entry in the parent directory + es = entries(search_path, identifier, + options = {:report_last_commit => false}) + es ? es.detect {|e| e.name == search_name} : nil + end + end + + def entries(path=nil, identifier=nil, options={}) + entries = Entries.new + trees = @g.trees(@project, path: path, ref_name: identifier) + trees.each do |tree| + entries << Entry.new({ + :name => tree.name, + :path => File.join(path,tree.name), + :kind => tree.type == 'tree' ? 'dir' : 'file', + :size => nil, + :lastrev => nil + }) + end + entries.sort_by_name + rescue ScmCommandAborted + nil + rescue Exception + nil + end + + def lastrev(path, rev) + return nil if path.nil? + cmd_args = %w|log --no-color --encoding=UTF-8 --date=iso --pretty=fuller --no-merges -n 1| + cmd_args << rev if rev + cmd_args << "--" << path unless path.empty? + lines = [] + git_cmd(cmd_args) { |io| lines = io.readlines } + begin + id = lines[0].split[1] + author = lines[1].match('Author:\s+(.*)$')[1] + time = Time.parse(lines[4].match('CommitDate:\s+(.*)$')[1]) + + Revision.new({ + :identifier => id, + :scmid => id, + :author => author, + :time => time, + :message => nil, + :paths => nil + }) + rescue NoMethodError => e + logger.error("The revision '#{path}' has a wrong format") + return nil + end + rescue ScmCommandAborted + nil + end + + def revisions(path, identifier_from, identifier_to, options={}) + revs = Revisions.new + cmd_args = %w|log --no-color --encoding=UTF-8 --raw --date=iso --pretty=fuller --parents --stdin| + cmd_args << "--reverse" if options[:reverse] + cmd_args << "-n" << "#{options[:limit].to_i}" if options[:limit] + cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) if path && !path.empty? + revisions = [] + if identifier_from || identifier_to + revisions << "" + revisions[0] << "#{identifier_from}.." if identifier_from + revisions[0] << "#{identifier_to}" if identifier_to + else + unless options[:includes].blank? + revisions += options[:includes] + end + unless options[:excludes].blank? + revisions += options[:excludes].map{|r| "^#{r}"} + end + end + + + commits = @g.commits(@project, {ref_name: identifier_to}) + commits.each do |commit| + revision = Revision.new({ + :identifier => commit.id, + :scmid => commit.id, + :author => commit.author_name, + :time => Time.parse(commit.created_at), + :message => commit.message, + :paths => nil, + :parents => nil + }) + revs << revision + end + + revs + rescue ScmCommandAborted => e + err_msg = "git log error: #{e.message}" + logger.error(err_msg) + if block_given? + raise CommandFailed, err_msg + else + revs + end + end + + def diff(path, identifier_from, identifier_to=nil) + path ||= '' + cmd_args = [] + if identifier_to + cmd_args << "diff" << "--no-color" << identifier_to << identifier_from + else + cmd_args << "show" << "--no-color" << identifier_from + end + cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) unless path.empty? + diff = [] + git_cmd(cmd_args) do |io| + io.each_line do |line| + diff << line + end + end + diff + rescue ScmCommandAborted + nil + end + + def annotate(path, identifier=nil) + identifier = 'HEAD' if identifier.blank? + cmd_args = %w|blame| + cmd_args << "-p" << identifier << "--" << scm_iconv(@path_encoding, 'UTF-8', path) + blame = Annotate.new + content = nil + git_cmd(cmd_args) { |io| io.binmode; content = io.read } + # git annotates binary files + return nil if content.is_binary_data? + identifier = '' + # git shows commit author on the first occurrence only + authors_by_commit = {} + content.split("\n").each do |line| + if line =~ /^([0-9a-f]{39,40})\s.*/ + identifier = $1 + elsif line =~ /^author (.+)/ + authors_by_commit[identifier] = $1.strip + elsif line =~ /^\t(.*)/ + blame.add_line($1, Revision.new( + :identifier => identifier, + :revision => identifier, + :scmid => identifier, + :author => authors_by_commit[identifier] + )) + identifier = '' + author = '' + end + end + blame + rescue ScmCommandAborted + nil + end + + def cat(path, identifier=nil) + if identifier.nil? + identifier = 'HEAD' + end + file = @g.files(@project, path, identifier) + cat = Base64.decode64 file.content + rescue ScmCommandAborted + nil + end + + def parse_commit(commits) + sum = {file: 0, insertion: 0, deletion: 0} + commits.split("\n").each do |commit| + if /(\d+)\s+?file/ =~ commit + sum[:file] += $1 .to_i + end + if /(\d+)\s+?insertion/ =~ commit + sum[:insertion] += $1.to_i + end + if /(\d+)\s+?deletion/ =~ commit + sum[:deletion] += $1.to_i + end + end + sum[:insertion] + sum[:deletion] + end + + def commits(authors, start_date, end_date, branch='master') + rs = [] + authors.each do |author| + cmd_args = %W|log #{branch} --pretty=tformat: --shortstat --author=#{author} --since=#{start_date} --until=#{end_date}| + commits = '' + git_cmd(cmd_args) do |io| + commits = io.read + end + logger.info "git log output for #{author} #{commits}" + rs << {author: author, num: parse_commit(commits)} + end + rs + end + + class Revision < Redmine::Scm::Adapters::Revision + # Returns the readable identifier + def format_identifier + identifier[0,8] + end + end + + def git_cmd(args, options = {}, &block) + logger.info "git cmd: #{args.join(' ')}" + end + private :git_cmd + end + + end + end +end diff --git a/lib/tasks/gitlab.rake b/lib/tasks/gitlab.rake new file mode 100644 index 000000000..ffa81e912 --- /dev/null +++ b/lib/tasks/gitlab.rake @@ -0,0 +1,44 @@ +require 'trustie/gitlab/sync' + +namespace :gitlab do + namespace :sync do + desc "sync users to gitlab" + task :users => :environment do + # User.where(username: 'root').find_each do |user| + s = Trustie::Gitlab::Sync.new + User.find_each do |user| + s.sync_user(user) + end + end + + desc "update user password" + task :password => :environment do + s = Trustie::Gitlab::Sync.new + s.change_password(1,'5188b7a65acf294ee7deceb397b6f9c62214ea50','dcb8d9fffabec60c2d0d1030b679fbbb') + end + + desc "sync projects to gitlab" + task :projects => :environment do + s = Trustie::Gitlab::Sync.new + Project.where(id: ENV["PROJECT_ID"]).find_each do |project| + s.sync_project(project, path: ENV["REP_NAME"], import_url: project.repository.url) + end + end + + desc "remove all projects" + task :remove_all_projects => :environment do + g = Gitlab.client + 100.times do + g.projects(scope: 'all').each do |p| + puts p.id + begin + g.delete_project(p.id) + rescue => e + puts e + end + end + end + end + + end +end diff --git a/lib/trustie.rb b/lib/trustie.rb index 3636efd95..2010e2d43 100644 --- a/lib/trustie.rb +++ b/lib/trustie.rb @@ -1,3 +1,4 @@ require 'trustie/utils' require 'trustie/utils/image' +require 'trustie/gitlab/api' require 'trustie/grack/grack' diff --git a/lib/trustie/gitlab/api.rb b/lib/trustie/gitlab/api.rb new file mode 100644 index 000000000..6c1993721 --- /dev/null +++ b/lib/trustie/gitlab/api.rb @@ -0,0 +1,35 @@ +#coding=utf-8 +# +# +module Trustie + module Gitlab + class Api + # attr_accessor :g + + def initialize(client=nil) + @g = client || ::Gitlab.client + end + + def trees(project_id) + @g.trees(project_id) + end + + def entries(project) + entries = [] + api = Trustie::Gitlab::Api.new + trees = api.trees(11) + trees.each do |tree| + entries << Redmine::Scm::Adapters::Entry.new({ + :name => tree.name, + :path => tree.id, + :kind => tree.type == 'tree' ? 'dir' : 'file', + :size => nil, + :lastrev => nil + }) + end + entries + end + + end + end +end diff --git a/lib/trustie/gitlab/helper.rb b/lib/trustie/gitlab/helper.rb new file mode 100644 index 000000000..5ea4c13e1 --- /dev/null +++ b/lib/trustie/gitlab/helper.rb @@ -0,0 +1,37 @@ +#coding=utf-8 + +module Trustie + module Gitlab + module Helper + def change_password(uid, en_pwd, salt) + options = {:encrypted_password=>en_pwd, :password_salt=>salt} + self.g.put("/users/ext/#{uid}", :body => options) + # g.edit_user(uid, :encrypted_password=>en_pwd, :password_salt=>salt) + end + + def add_user(user) + u = nil + begin + u = self.g.get("/users?search=#{user.mail}").first + unless u + u = self.g.create_user(user.mail, + user.hashed_password, + name: user.show_name, + username: user.login, + confirm: "true") + user.gid = u.id + end + change_password(u.id, user.hashed_password, user.salt) + rescue => e + puts e + end + return u + end + + def del_user(user) + ## gitlab unimplement + end + + end + end +end \ No newline at end of file diff --git a/lib/trustie/gitlab/manage_member.rb b/lib/trustie/gitlab/manage_member.rb new file mode 100644 index 000000000..d0f74ad5e --- /dev/null +++ b/lib/trustie/gitlab/manage_member.rb @@ -0,0 +1,56 @@ +#coding=utf-8 +# +# +module Trustie + module Gitlab + + module ManageMember + def self.included(base) + base.class_eval { + before_create :add_gitlab_member + before_destroy :delete_gitlab_member + after_save :change_gitlab_member + } + end + + def change_gitlab_member + if isGitlabProject? + @g ||= ::Gitlab.client + @g.edit_team_member(project.gpid, self.member.user.gid, self.role.to_gitlab_role ) + end + end + + def add_gitlab_member + if isGitlabProject? + @g ||= ::Gitlab.client + @g.add_team_member(project.gpid, self.member.user.gid, self.role.to_gitlab_role ) + end + end + + def delete_gitlab_member + if isGitlabProject? + if member.roles.count <=1 + @g ||= ::Gitlab.client + @g.remove_team_member(project.gpid, self.member.user.gid) + end + end + end + + private + def project + self.member.project + end + + def repository + project.repository + end + + def isGitlabProject? + repository && repository.gitlab? + end + + end + + end +end + diff --git a/lib/trustie/gitlab/manage_user.rb b/lib/trustie/gitlab/manage_user.rb new file mode 100644 index 000000000..76528739c --- /dev/null +++ b/lib/trustie/gitlab/manage_user.rb @@ -0,0 +1,37 @@ +#coding=utf-8 +# +# +require_relative 'helper' +module Trustie + module Gitlab + module ManageUser + include Helper + + def self.included(base) + base.class_eval { + before_create :add_gitlab_user + before_destroy :delete_gitlab_user + before_save :change_gitlab_user + } + end + + def add_gitlab_user + add_user(self) + end + + def delete_gitlab_user + del_user(self) + end + + def change_gitlab_user + change_password(self.gid, self.hashed_password, self.salt) + end + + private + def g + @g ||= ::Gitlab.client + end + + end + end +end diff --git a/lib/trustie/gitlab/sync.rb b/lib/trustie/gitlab/sync.rb new file mode 100644 index 000000000..d941795ee --- /dev/null +++ b/lib/trustie/gitlab/sync.rb @@ -0,0 +1,73 @@ +#coding=utf-8 + +require_relative 'helper' + +module Trustie + module Gitlab + module UserLevel + GUEST = 10 + REPORTER = 20 + DEVELOPER = 30 + MASTER = 40 + OWNER = 50 + end + + class Sync + attr :g + include Helper + + def initialize + @g = ::Gitlab.client + end + + def sync_user(user) + u = add_user(user) + user.save! if u + end + + def sync_project(project, opt={}) + gid = project.owner.gid + raise "unknow gid" unless gid + path = opt[:path] + raise "unknow path" unless path + import_url = opt[:import_url] + raise "unknow import_url" unless import_url + + if opt[:password] + import_url.sub('@', ":#{opt[:password]}@") + end + + # import url http://xianbo_trustie2:1234@repository.trustie.net/xianbo/trustie2.git + # can use password + gproject = self.g.create_project(path, + path: path, + description: project.description, + wiki_enabled: false, + wall_enabled: false, + issues_enabled: false, + snippets_enabled: false, + public: false, + user_id: gid, + import_url: import_url + ) + project.gpid = gproject.id + project.save! + puts "Successfully created #{project.name}" + # add team members + # + + project.members.each do |m| + begin + self.g.add_team_member(gproject.id, m.user.gid, UserLevel::DEVELOPER) + rescue => e + puts e + end + end + end + + def remove_project + end + end + + end +end \ No newline at end of file diff --git a/public/images/vlicon/branch_icon.png b/public/images/vlicon/branch_icon.png new file mode 100644 index 000000000..c80e17134 Binary files /dev/null and b/public/images/vlicon/branch_icon.png differ diff --git a/public/images/vlicon/clone_url.png b/public/images/vlicon/clone_url.png new file mode 100644 index 000000000..a1c71862a Binary files /dev/null and b/public/images/vlicon/clone_url.png differ diff --git a/public/images/vlicon/commit_icon.png b/public/images/vlicon/commit_icon.png new file mode 100644 index 000000000..148dbcd4b Binary files /dev/null and b/public/images/vlicon/commit_icon.png differ diff --git a/public/images/vlicon/download_icon.png b/public/images/vlicon/download_icon.png new file mode 100644 index 000000000..b442730fe Binary files /dev/null and b/public/images/vlicon/download_icon.png differ diff --git a/public/images/vlicon/fork_icon.png b/public/images/vlicon/fork_icon.png new file mode 100644 index 000000000..45a6b0e82 Binary files /dev/null and b/public/images/vlicon/fork_icon.png differ diff --git a/public/javascripts/project.js b/public/javascripts/project.js index c74ff18bf..5ba7c7145 100644 --- a/public/javascripts/project.js +++ b/public/javascripts/project.js @@ -511,3 +511,13 @@ function submitProjectFeedback() { $("#project_feedback_form").submit(); } +// 点击按钮复制功能 +function jsCopy(){ + var e=document.getElementById("copy_rep_content"); + e.select(); + document.execCommand("Copy"); +} + +function zip(){ + alert("该功能正在紧张的开发中,我们会争取在最短时间内上线,如若对您工作造成不便敬请谅解!") +} \ No newline at end of file diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index 727d79538..f8b5395d5 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -17,6 +17,15 @@ li{list-style-type:none;} .cancel_btn {background-color: #c1c1c1; color: #ffffff; padding: 2px 5px; border: none; border-radius: 3px; cursor: pointer;} .cancel_btn:hover {background-color:#656565; } /*huang*/ +.repository-update-dec{ + padding: 10px; +} +.repository-update-dec .c_grey { + color: #999999; +} +.repository-update-dec .c_orange { + color: #ff7143; +} .hwork_input_news{ border:1px solid #64bdd9; height:22px; width:594px; background:#fff; margin-bottom:10px; padding:5px;} .ml55{ margin-left:55px;} diff --git a/public/stylesheets/project.css b/public/stylesheets/project.css index 1c31f603f..e769e14e4 100644 --- a/public/stylesheets/project.css +++ b/public/stylesheets/project.css @@ -13,6 +13,7 @@ a:hover.lg-foot{ color:#787b7e;} /*右侧内容--动态*/ .project_r_h{ width:670px; height:40px; background:#eaeaea; margin-bottom:10px;} .project_h2{ background:#64bdd9; color:#fff; height:33px; width:90px; text-align:center; font-weight:normal; padding-top:7px; font-size:16px;} +.project_h2_repository{ background:#64bdd9; color:#fff; height:33px; width:auto; text-align:center; font-weight:normal; padding-top:7px; font-size:16px;} .project_h22{ background:#64bdd9; color:#fff; height:33px; width:124px; text-align:center; font-weight:normal; padding-top:7px; font-size:16px;} .project_r_box{ border:1px solid #e2e1e1; width:670px; margin-top:10px;} .project_h3 { color:#646464; font-size:14px; padding:0 10px; border-bottom:1px solid #e2e1e1;} @@ -814,8 +815,8 @@ div.changeset { border-bottom: 1px solid #ddd; } /*****项目版本库文件 Tables *****/ -.autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em;} -tr.entry { border: 1px solid #f8f8f8; } +.autoscroll {overflow-x: auto; margin-bottom: 0.2em;} +tr.entry { border: 1px solid #DDD; } tr.entry td { white-space: nowrap; } tr.entry td.filename { width: 30%; } tr.entry td.filename_no_report { width: 70%; } diff --git a/public/stylesheets/public.css b/public/stylesheets/public.css index 91b073233..df42f783e 100644 --- a/public/stylesheets/public.css +++ b/public/stylesheets/public.css @@ -12,7 +12,7 @@ textarea {resize: none;} .pInline {margin:0px; padding:0px; display:inline-block;} /*常用*/ -select,input,textarea{ border:1px solid #269ac9; background:#fff; color:#000; padding-left:5px; } +select,input,textarea{ border:1px solid #269ac9; background:#fff; color:#000; padding-left:5px;padding-right: 5px; } .sub_btn{ cursor:pointer; -moz-border-radius:3px; -webkit-border-radius:3px; border:1px solid #707070; color:#000; border-radius:3px; padding:1px 10px; margin-bottom:10px; background:#dbdbdb;} .sub_btn:hover{ background:#b5e2fa; color:#000; border:1px solid #3c7fb1;} table{ background:#fff;} @@ -104,6 +104,7 @@ h4{ font-size:14px; color:#3b3b3b;} .mt8{ margin-top:8px;} .mt10{ margin-top:10px !important;} .mt30{ margin-top: 30px;} +.mt40{ margin-top: 40px;} .mt12 { margin-top:12px !important;} .mt15 {margin-top:15px;} .mt19 {margin-top:19px !important;} @@ -116,6 +117,8 @@ h4{ font-size:14px; color:#3b3b3b;} .mb20{ margin-bottom:20px;} .pl15{ padding-left:15px;} .pt5{ padding-top:5px;} +.pt10{ padding-top:10px;} +.pb5{ padding-bottom: 5px;} .w20{ width:20px;} .w40{width: 40px;} .w45{ width: 45px;} diff --git a/public/stylesheets/repository.css b/public/stylesheets/repository.css new file mode 100644 index 000000000..375b188d3 --- /dev/null +++ b/public/stylesheets/repository.css @@ -0,0 +1,221 @@ +.git_usr_title{ + margin: 0px; + overflow: hidden; + font-size: 18px; + font-weight: bold; + text-overflow: ellipsis; + vertical-align: top; + white-space: nowrap; + padding: 0px 10px; +} +.overall-summary{ + position: relative; + margin-bottom: 10px; + border: 1px solid #DDD; + border-radius: 3px; +} +.overall-summary .overall-summary-bottomless{ + margin-bottom: 0px; + border-bottom: 0px none; + border-radius: 3px 3px 0px 0px; +} +.stats-switcher-viewport{ + height: 38px; + overflow: hidden; +} +.stats-switcher-viewport .stats-switcher-wrapper{ + position: relative; + top: 0px; + transition: top 0.25s ease-in-out 0s; +} +.numbers-summary{ + display: table; + width: 100%; + table-layout: fixed; + margin-top: 9px; +} +.numbers-summary li{ + display: table-cell; + padding: 0px; + margin: 0px; + text-align: center; + white-space: nowrap; +} +.numbers-summary .octicon { + color: #999; +} +.text-emphasized { + font-weight: bold; + color: #333; +} +.octicon .octicon-history { + font: 16px/1 octicons; + display: inline-block; + text-decoration: none; + text-rendering: auto; + -moz-user-select: none; +} +.select2-container { + margin: 0px; + position: relative; + display: inline-block; + vertical-align: middle; +} + +.select2-container .select2-choice { + display: block; + height: 26px; + padding: 0px 0px 0px 8px; + overflow: hidden; + position: relative; + border: 1px solid #AAA; + white-space: nowrap; + line-height: 26px; + color: #444; + text-decoration: none; + border-radius: 4px; + background-clip: padding-box; + -moz-user-select: none; + background-color: #FFF; + background-image: linear-gradient(to top, #EEE 0%, #FFF 50%); +} +.repository-title-dec{ + color: #fff !important; +} +.repository-url{ + color: #2D2D2D; + margin: auto 0px; + text-align: center; + font-size: 14px; + margin-bottom: 10px; + margin-top: 10px; +} +.center{ + text-align: center; +} +.light-well { + background: #F9F9F9 none repeat scroll 0% 0%; + padding: 15px; +} +.page-title { + margin-top: 0px; + line-height: 1.5; + font-weight: bold; + margin-bottom: 5px; + font-size: 18px; +} +/************* CodeRay styles *************/ +.syntaxhl div {display: inline;} +.syntaxhl .line-numbers {padding: 2px 4px 2px 4px; background-color: #eee; margin:0px 5px 0px 0px;} +.syntaxhl .code pre { overflow: auto } +.syntaxhl .debug { color: white !important; background: blue !important; } + +.syntaxhl .annotation { color:#007 } +.syntaxhl .attribute-name { color:#b48 } +.syntaxhl .attribute-value { color:#700 } +.syntaxhl .binary { color:#509 } +.syntaxhl .char .content { color:#D20 } +.syntaxhl .char .delimiter { color:#710 } +.syntaxhl .char { color:#D20 } +.syntaxhl .class { color:#258; font-weight:bold } +.syntaxhl .class-variable { color:#369 } +.syntaxhl .color { color:#0A0 } +.syntaxhl .comment { color:#385 } +.syntaxhl .comment .char { color:#385 } +.syntaxhl .comment .delimiter { color:#385 } +.syntaxhl .complex { color:#A08 } +.syntaxhl .constant { color:#258; font-weight:bold } +.syntaxhl .decorator { color:#B0B } +.syntaxhl .definition { color:#099; font-weight:bold } +.syntaxhl .delimiter { color:black } +.syntaxhl .directive { color:#088; font-weight:bold } +.syntaxhl .doc { color:#970 } +.syntaxhl .doc-string { color:#D42; font-weight:bold } +.syntaxhl .doctype { color:#34b } +.syntaxhl .entity { color:#800; font-weight:bold } +.syntaxhl .error { color:#F00; background-color:#FAA } +.syntaxhl .escape { color:#666 } +.syntaxhl .exception { color:#C00; font-weight:bold } +.syntaxhl .float { color:#06D } +.syntaxhl .function { color:#06B; font-weight:bold } +.syntaxhl .global-variable { color:#d70 } +.syntaxhl .hex { color:#02b } +.syntaxhl .imaginary { color:#f00 } +.syntaxhl .include { color:#B44; font-weight:bold } +.syntaxhl .inline { background-color: hsla(0,0%,0%,0.07); color: black } +.syntaxhl .inline-delimiter { font-weight: bold; color: #666 } +.syntaxhl .instance-variable { color:#33B } +.syntaxhl .integer { color:#06D } +.syntaxhl .key .char { color: #60f } +.syntaxhl .key .delimiter { color: #404 } +.syntaxhl .key { color: #606 } +.syntaxhl .keyword { color:#939; font-weight:bold } +.syntaxhl .label { color:#970; font-weight:bold } +.syntaxhl .local-variable { color:#963 } +.syntaxhl .namespace { color:#707; font-weight:bold } +.syntaxhl .octal { color:#40E } +.syntaxhl .operator { } +.syntaxhl .predefined { color:#369; font-weight:bold } +.syntaxhl .predefined-constant { color:#069 } +.syntaxhl .predefined-type { color:#0a5; font-weight:bold } +.syntaxhl .preprocessor { color:#579 } +.syntaxhl .pseudo-class { color:#00C; font-weight:bold } +.syntaxhl .regexp .content { color:#808 } +.syntaxhl .regexp .delimiter { color:#404 } +.syntaxhl .regexp .modifier { color:#C2C } +.syntaxhl .regexp { background-color:hsla(300,100%,50%,0.06); } +.syntaxhl .reserved { color:#080; font-weight:bold } +.syntaxhl .shell .content { color:#2B2 } +.syntaxhl .shell .delimiter { color:#161 } +.syntaxhl .shell { background-color:hsla(120,100%,50%,0.06); } +.syntaxhl .string .char { color: #46a } +.syntaxhl .string .content { color: #46a } +.syntaxhl .string .delimiter { color: #46a } +.syntaxhl .string .modifier { color: #46a } +.syntaxhl .symbol .content { color:#d33 } +.syntaxhl .symbol .delimiter { color:#d33 } +.syntaxhl .symbol { color:#d33 } +.syntaxhl .tag { color:#070 } +.syntaxhl .type { color:#339; font-weight:bold } +.syntaxhl .value { color: #088; } +.syntaxhl .variable { color:#037 } + +.syntaxhl .insert { background: hsla(120,100%,50%,0.12) } +.syntaxhl .delete { background: hsla(0,100%,50%,0.12) } +.syntaxhl .change { color: #bbf; background: #007; } +.syntaxhl .head { color: #f8f; background: #505 } +.syntaxhl .head .filename { color: white; } + +.syntaxhl .delete .eyecatcher { background-color: hsla(0,100%,50%,0.2); border: 1px solid hsla(0,100%,45%,0.5); margin: -1px; border-bottom: none; border-top-left-radius: 5px; border-top-right-radius: 5px; } +.syntaxhl .insert .eyecatcher { background-color: hsla(120,100%,50%,0.2); border: 1px solid hsla(120,100%,25%,0.5); margin: -1px; border-top: none; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; } + +.syntaxhl .insert .insert { color: #0c0; background:transparent; font-weight:bold } +.syntaxhl .delete .delete { color: #c00; background:transparent; font-weight:bold } +.syntaxhl .change .change { color: #88f } +.syntaxhl .head .head { color: #f4f } + +/***** Media print specific styles *****/ +@media print { + #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; } + #main { background: #fff; } + #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;} + #wiki_add_attachment { display:none; } + .hide-when-print { display: none; } + .autoscroll {overflow-x: visible;} + table.list {margin-top:0.5em;} + table.list th, table.list td {border: 1px solid #aaa;} +} + +.cloneUrl {width:235px; height:21px; border:1px solid #dddddd; outline:none; overflow:hidden; line-height:21px; resize:none;white-space:nowrap;} +.clone_btn {width:30px; height:21px; border-top:1px solid #dddddd; border-bottom:1px solid #dddddd; border-right:1px solid #dddddd; outline:none; float:left; background-image:linear-gradient(#FCFCFC, #EEE); text-align:center;} +.vl_btn {height:21px; padding:0px 5px; vertical-align:middle; border:1px solid #dddddd; float:left; line-height:21px; background-image:linear-gradient(#FCFCFC, #EEE);} +.vl_btn_2 {height:21px; padding:0px 5px; vertical-align:middle; border-top:1px solid #dddddd; border-bottom:1px solid #dddddd; border-right:1px solid #dddddd; float:left; line-height:21px;} +.recordBanner {width:670px; height:30px; background-color:#f1f1f1; color:#666666; line-height:30px; vertical-align:middle;} +.vl_copy {background:url(../images/vlicon/clone_url.png) 0px 0px no-repeat; padding-left:22px;} +.vl_zip {background:url(../images/vlicon/download_icon.png) 0px 0px no-repeat; padding-left:22px;} +.vl_fork {background:url(../images/vlicon/fork_icon.png) 0px -2px no-repeat; padding-left:22px;} +.vl_commit {background:url(../images/vlicon/commit_icon.png) 0px -2px no-repeat; padding-left:22px;weight:20px;height: 24px;} +.vl_branch {background:url(../images/vlicon/branch_icon.png) 0px -2px no-repeat; padding-left:22px} +.mt1 {margin-top:1px;} +.mt2 {margin-top:2px;} +.commit_content_dec{width: 300px;overflow: hidden; white-space: nowrap;text-overflow: ellipsis;} diff --git a/spec/requests/gitlab_request_spec.rb b/spec/requests/gitlab_request_spec.rb new file mode 100644 index 000000000..05a94568b --- /dev/null +++ b/spec/requests/gitlab_request_spec.rb @@ -0,0 +1,13 @@ +require 'rails_helper' + +RSpec.describe "Gitlab request", :type => :request do + + describe "get repository files" do + it "参数正确,可以获取正确列表 " do + api = Trustie::Gitlab::Api.new + trees = api.trees(11) + expect(trees).to be_instance_of(Array) + end + end +end +
    <%= l(:field_name) %><%= l(:field_filesize) %><%= l(:label_revision) %><%= l(:label_age) %><%= l(:field_author) %><%= l(:field_comments) %>
    "> <% if entry.is_dir? %> +<%# 展开文件目录 %>   <% 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)}")%> <%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %><%= link_to_revision(entry.changeset, @repository) if entry.changeset %> <%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %>