From 0a70aca3b177cffccd2e04579712665d10b8fd69 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 7 Jun 2012 15:44:57 +0300 Subject: [PATCH] Models Refactoring: Move methods to roles --- app/models/event.rb | 4 +- app/models/event/push_trait.rb | 92 ------------------ app/models/issue.rb | 7 +- app/models/key.rb | 20 +--- app/models/merge_request.rb | 8 +- app/models/project.rb | 80 ++++++++++++---- app/models/project/hooks_trait.rb | 109 --------------------- app/models/project/permissions_trait.rb | 60 ------------ app/models/project/repository_trait.rb | 122 ------------------------ app/models/project/validations_trait.rb | 43 --------- app/models/user.rb | 50 +--------- app/roles/account.rb | 49 ++++++++++ app/roles/authority.rb | 58 +++++++++++ app/roles/git_merge.rb | 2 + app/roles/git_push.rb | 107 +++++++++++++++++++++ app/roles/push_event.rb | 90 +++++++++++++++++ app/roles/repository.rb | 120 +++++++++++++++++++++++ app/roles/ssh_key.rb | 18 ++++ app/roles/team.rb | 10 ++ app/roles/upvote.rb | 6 ++ 20 files changed, 531 insertions(+), 524 deletions(-) delete mode 100644 app/models/event/push_trait.rb delete mode 100644 app/models/project/hooks_trait.rb delete mode 100644 app/models/project/permissions_trait.rb delete mode 100644 app/models/project/repository_trait.rb delete mode 100644 app/models/project/validations_trait.rb create mode 100644 app/roles/account.rb create mode 100644 app/roles/authority.rb create mode 100644 app/roles/git_merge.rb create mode 100644 app/roles/git_push.rb create mode 100644 app/roles/push_event.rb create mode 100644 app/roles/repository.rb create mode 100644 app/roles/ssh_key.rb create mode 100644 app/roles/team.rb create mode 100644 app/roles/upvote.rb diff --git a/app/models/event.rb b/app/models/event.rb index cf8dc153..3f1ff3e5 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,4 +1,6 @@ class Event < ActiveRecord::Base + include PushEvent + default_scope where("author_id IS NOT NULL") Created = 1 @@ -9,8 +11,6 @@ class Event < ActiveRecord::Base Commented = 6 Merged = 7 - does "event/push" - belongs_to :project belongs_to :target, :polymorphic => true diff --git a/app/models/event/push_trait.rb b/app/models/event/push_trait.rb deleted file mode 100644 index f12d50bd..00000000 --- a/app/models/event/push_trait.rb +++ /dev/null @@ -1,92 +0,0 @@ -module Event::PushTrait - as_trait do - def valid_push? - data[:ref] - rescue => ex - false - end - - def tag? - data[:ref]["refs/tags"] - end - - def new_branch? - commit_from =~ /^00000/ - end - - def new_ref? - commit_from =~ /^00000/ - end - - def rm_ref? - commit_to =~ /^00000/ - end - - def md_ref? - !(rm_ref? || new_ref?) - end - - def commit_from - data[:before] - end - - def commit_to - data[:after] - end - - def ref_name - if tag? - tag_name - else - branch_name - end - end - - def branch_name - @branch_name ||= data[:ref].gsub("refs/heads/", "") - end - - def tag_name - @tag_name ||= data[:ref].gsub("refs/tags/", "") - end - - # Max 20 commits from push DESC - def commits - @commits ||= data[:commits].map { |commit| project.commit(commit[:id]) }.reverse - end - - def commits_count - data[:total_commits_count] || commits.count || 0 - end - - def ref_type - tag? ? "tag" : "branch" - end - - def push_action_name - if new_ref? - "pushed new" - elsif rm_ref? - "removed #{ref_type}" - else - "pushed to" - end - end - - def parent_commit - project.commit(commit_from) - rescue => ex - nil - end - - def last_commit - project.commit(commit_to) - rescue => ex - nil - end - - def push_with_commits? - md_ref? && commits.any? && parent_commit && last_commit - end - end -end diff --git a/app/models/issue.rb b/app/models/issue.rb index 5fe0e0b2..3ed47ef1 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -1,4 +1,6 @@ class Issue < ActiveRecord::Base + include Upvote + belongs_to :project belongs_to :milestone belongs_to :author, :class_name => "User" @@ -53,11 +55,6 @@ class Issue < ActiveRecord::Base def new? today? && created_at == updated_at end - - # Return the number of +1 comments (upvotes) - def upvotes - notes.select(&:upvote?).size - end end # == Schema Information # diff --git a/app/models/key.rb b/app/models/key.rb index 8fe8716c..2b05ef55 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -1,6 +1,7 @@ require 'digest/md5' class Key < ActiveRecord::Base + include SshKey belongs_to :user belongs_to :project @@ -37,28 +38,11 @@ class Key < ActiveRecord::Base end end - def update_repository - Gitlab::GitHost.system.new.configure do |c| - c.update_keys(identifier, key) - c.update_projects(projects) - end - end - - def repository_delete_key - Gitlab::GitHost.system.new.configure do |c| - #delete key file is there is no identically deploy keys - if !is_deploy_key || Key.where(:identifier => identifier).count() == 0 - c.delete_key(identifier) - end - c.update_projects(projects) - end - end - def is_deploy_key true if project_id end - #projects that has this key + # projects that has this key def projects if is_deploy_key [project] diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 2aba24bc..d532373f 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1,6 +1,8 @@ require File.join(Rails.root, "app/models/commit") class MergeRequest < ActiveRecord::Base + include Upvote + UNCHECKED = 1 CAN_BE_MERGED = 2 CANNOT_BE_MERGED = 3 @@ -128,12 +130,6 @@ class MergeRequest < ActiveRecord::Base self.project.events.where(:target_id => self.id, :target_type => "MergeRequest", :action => Event::Closed).last end - - # Return the number of +1 comments (upvotes) - def upvotes - notes.select(&:upvote?).size - end - def commits st_commits || [] end diff --git a/app/models/project.rb b/app/models/project.rb index 72cc833c..12556d35 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,13 +1,15 @@ require "grit" class Project < ActiveRecord::Base + include Repository + include GitPush + include Authority + include Team + + # + # Relations + # belongs_to :owner, :class_name => "User" - - does "project/validations" - does "project/repository" - does "project/permissions" - does "project/hooks" - has_many :users, :through => :users_projects has_many :events, :dependent => :destroy has_many :merge_requests, :dependent => :destroy @@ -21,8 +23,14 @@ class Project < ActiveRecord::Base has_many :wikis, :dependent => :destroy has_many :protected_branches, :dependent => :destroy + # + # Protected attributes + # attr_protected :private_flag, :owner_id + # + # Scopes + # scope :public_only, where(:private_flag => false) scope :without_user, lambda { |user| where("id not in (:ids)", :ids => user.projects.map(&:id) ) } @@ -30,14 +38,57 @@ class Project < ActiveRecord::Base joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC") end - def self.access_options - UsersProject.access_roles - end - def self.search query where("name like :query or code like :query or path like :query", :query => "%#{query}%") end + # + # Validations + # + validates :name, + :uniqueness => true, + :presence => true, + :length => { :within => 0..255 } + + validates :path, + :uniqueness => true, + :presence => true, + :format => { :with => /^[a-zA-Z0-9_\-\.]*$/, + :message => "only letters, digits & '_' '-' '.' allowed" }, + :length => { :within => 0..255 } + + validates :description, + :length => { :within => 0..2000 } + + validates :code, + :presence => true, + :uniqueness => true, + :format => { :with => /^[a-zA-Z0-9_\-\.]*$/, + :message => "only letters, digits & '_' '-' '.' allowed" }, + :length => { :within => 1..255 } + + validates :owner, :presence => true + validate :check_limit + validate :repo_name + + def check_limit + unless owner.can_create_project? + errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it") + end + rescue + errors[:base] << ("Cant check your ability to create project") + end + + def repo_name + if path == "gitolite-admin" + errors.add(:path, " like 'gitolite-admin' is not allowed") + end + end + + def self.access_options + UsersProject.access_roles + end + def to_param code end @@ -46,15 +97,6 @@ class Project < ActiveRecord::Base [GIT_HOST['host'], code].join("/") end - def team_member_by_name_or_email(email = nil, name = nil) - user = users.where("email like ? or name like ?", email, name).first - users_projects.find_by_user_id(user.id) if user - end - - def team_member_by_id(user_id) - users_projects.find_by_user_id(user_id) - end - def common_notes notes.where(:noteable_type => ["", nil]).inc_author_project end diff --git a/app/models/project/hooks_trait.rb b/app/models/project/hooks_trait.rb deleted file mode 100644 index 2f97eb6c..00000000 --- a/app/models/project/hooks_trait.rb +++ /dev/null @@ -1,109 +0,0 @@ -module Project::HooksTrait - as_trait do - def observe_push(oldrev, newrev, ref, user) - data = post_receive_data(oldrev, newrev, ref, user) - - Event.create( - :project => self, - :action => Event::Pushed, - :data => data, - :author_id => data[:user_id] - ) - end - - def update_merge_requests(oldrev, newrev, ref, user) - return true unless ref =~ /heads/ - branch_name = ref.gsub("refs/heads/", "") - c_ids = self.commits_between(oldrev, newrev).map(&:id) - - # Update code for merge requests - mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all - mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } - - # Close merge requests - mrs = self.merge_requests.opened.where(:target_branch => branch_name).all - mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } - mrs.each { |merge_request| merge_request.merge!(user.id) } - - true - end - - def execute_web_hooks(oldrev, newrev, ref, user) - ref_parts = ref.split('/') - - # Return if this is not a push to a branch (e.g. new commits) - return if ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000" - - data = post_receive_data(oldrev, newrev, ref, user) - - web_hooks.each { |web_hook| web_hook.execute(data) } - end - - def post_receive_data(oldrev, newrev, ref, user) - - push_commits = commits_between(oldrev, newrev) - - # Total commits count - push_commits_count = push_commits.size - - # Get latest 20 commits ASC - push_commits_limited = push_commits.last(20) - - # Hash to be passed as post_receive_data - data = { - before: oldrev, - after: newrev, - ref: ref, - user_id: user.id, - user_name: user.name, - repository: { - name: name, - url: web_url, - description: description, - homepage: web_url, - }, - commits: [], - total_commits_count: push_commits_count - } - - # For perfomance purposes maximum 20 latest commits - # will be passed as post receive hook data. - # - push_commits_limited.each do |commit| - data[:commits] << { - id: commit.id, - message: commit.safe_message, - timestamp: commit.date.xmlschema, - url: "http://#{GIT_HOST['host']}/#{code}/commits/#{commit.id}", - author: { - name: commit.author_name, - email: commit.author_email - } - } - end - - data - end - - - # This method will be called after each post receive - # and only if autor_key_id present in gitlab. - # All callbacks for post receive should be placed here - # - def trigger_post_receive(oldrev, newrev, ref, author_key_id) - user = Key.find_by_identifier(author_key_id).user - - # Create push event - self.observe_push(oldrev, newrev, ref, user) - - # Close merged MR - self.update_merge_requests(oldrev, newrev, ref, user) - - # Execute web hooks - self.execute_web_hooks(oldrev, newrev, ref, user) - - # Create satellite - self.satellite.create unless self.satellite.exists? - end - end -end diff --git a/app/models/project/permissions_trait.rb b/app/models/project/permissions_trait.rb deleted file mode 100644 index 3b90c711..00000000 --- a/app/models/project/permissions_trait.rb +++ /dev/null @@ -1,60 +0,0 @@ -module Project::PermissionsTrait - as_trait do - # Compatible with all access rights - # Should be rewrited for new access rights - def add_access(user, *access) - access = if access.include?(:admin) - { :project_access => UsersProject::MASTER } - elsif access.include?(:write) - { :project_access => UsersProject::DEVELOPER } - else - { :project_access => UsersProject::REPORTER } - end - opts = { :user => user } - opts.merge!(access) - users_projects.create(opts) - end - - def reset_access(user) - users_projects.where(:project_id => self.id, :user_id => user.id).destroy if self.id - end - - def repository_readers - keys = Key.joins({:user => :users_projects}). - where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::REPORTER) - keys.map(&:identifier) + deploy_keys.map(&:identifier) - end - - def repository_writers - keys = Key.joins({:user => :users_projects}). - where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::DEVELOPER) - keys.map(&:identifier) - end - - def repository_masters - keys = Key.joins({:user => :users_projects}). - where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::MASTER) - keys.map(&:identifier) - end - - def allow_read_for?(user) - !users_projects.where(:user_id => user.id).empty? - end - - def guest_access_for?(user) - !users_projects.where(:user_id => user.id).empty? - end - - def report_access_for?(user) - !users_projects.where(:user_id => user.id, :project_access => [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty? - end - - def dev_access_for?(user) - !users_projects.where(:user_id => user.id, :project_access => [UsersProject::DEVELOPER, UsersProject::MASTER]).empty? - end - - def master_access_for?(user) - !users_projects.where(:user_id => user.id, :project_access => [UsersProject::MASTER]).empty? || owner_id == user.id - end - end -end diff --git a/app/models/project/repository_trait.rb b/app/models/project/repository_trait.rb deleted file mode 100644 index 2b9cf437..00000000 --- a/app/models/project/repository_trait.rb +++ /dev/null @@ -1,122 +0,0 @@ -module Project::RepositoryTrait - as_trait do - def valid_repo? - repo - rescue - errors.add(:path, "Invalid repository path") - false - end - - def commit(commit_id = nil) - Commit.find_or_first(repo, commit_id, root_ref) - end - - def fresh_commits(n = 10) - Commit.fresh_commits(repo, n) - end - - def commits_with_refs(n = 20) - Commit.commits_with_refs(repo, n) - end - - def commits_since(date) - Commit.commits_since(repo, date) - end - - def commits(ref, path = nil, limit = nil, offset = nil) - Commit.commits(repo, ref, path, limit, offset) - end - - def commits_between(from, to) - Commit.commits_between(repo, from, to) - end - - def write_hooks - %w(post-receive).each do |hook| - write_hook(hook, File.read(File.join(Rails.root, 'lib', "#{hook}-hook"))) - end - end - - def satellite - @satellite ||= Gitlab::Satellite.new(self) - end - - def write_hook(name, content) - hook_file = File.join(path_to_repo, 'hooks', name) - - File.open(hook_file, 'w') do |f| - f.write(content) - end - - File.chmod(0775, hook_file) - end - - def has_post_receive_file? - hook_file = File.join(path_to_repo, 'hooks', 'post-receive') - File.exists?(hook_file) - end - - def tags - repo.tags.map(&:name).sort.reverse - end - - def repo - @repo ||= Grit::Repo.new(path_to_repo) - end - - def url_to_repo - Gitlab::GitHost.url_to_repo(path) - end - - def path_to_repo - File.join(GIT_HOST["base_path"], "#{path}.git") - end - - def update_repository - Gitlab::GitHost.system.update_project(path, self) - - write_hooks if File.exists?(path_to_repo) - end - - def destroy_repository - Gitlab::GitHost.system.destroy_project(self) - end - - def repo_exists? - @repo_exists ||= (repo && !repo.branches.empty?) - rescue - @repo_exists = false - end - - def heads - @heads ||= repo.heads - end - - def tree(fcommit, path = nil) - fcommit = commit if fcommit == :head - tree = fcommit.tree - path ? (tree / path) : tree - end - - def open_branches - if protected_branches.empty? - self.repo.heads - else - pnames = protected_branches.map(&:name) - self.repo.heads.reject { |h| pnames.include?(h.name) } - end.sort_by(&:name) - end - - def has_commits? - !!commit - end - - def root_ref - default_branch || "master" - end - - def root_ref? branch - root_ref == branch - end - end -end diff --git a/app/models/project/validations_trait.rb b/app/models/project/validations_trait.rb deleted file mode 100644 index d2d207c4..00000000 --- a/app/models/project/validations_trait.rb +++ /dev/null @@ -1,43 +0,0 @@ -module Project::ValidationsTrait - as_trait do - validates :name, - :uniqueness => true, - :presence => true, - :length => { :within => 0..255 } - - validates :path, - :uniqueness => true, - :presence => true, - :format => { :with => /^[a-zA-Z0-9_\-\.]*$/, - :message => "only letters, digits & '_' '-' '.' allowed" }, - :length => { :within => 0..255 } - - validates :description, - :length => { :within => 0..2000 } - - validates :code, - :presence => true, - :uniqueness => true, - :format => { :with => /^[a-zA-Z0-9_\-\.]*$/, - :message => "only letters, digits & '_' '-' '.' allowed" }, - :length => { :within => 1..255 } - - validates :owner, :presence => true - validate :check_limit - validate :repo_name - - def check_limit - unless owner.can_create_project? - errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it") - end - rescue - errors[:base] << ("Cant check your ability to create project") - end - - def repo_name - if path == "gitolite-admin" - errors.add(:path, " like 'gitolite-admin' is not allowed") - end - end - end -end diff --git a/app/models/user.rb b/app/models/user.rb index 21b788d3..50854bf9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,6 @@ class User < ActiveRecord::Base + include Account + devise :database_authenticatable, :token_authenticatable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable @@ -65,30 +67,6 @@ class User < ActiveRecord::Base where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') end - def identifier - email.gsub /[@.]/, "_" - end - - def is_admin? - admin - end - - def require_ssh_key? - keys.count == 0 - end - - def can_create_project? - projects_limit > my_own_projects.count - end - - def last_activity_project - projects.first - end - - def first_name - name.split.first unless name.blank? - end - def self.find_for_ldap_auth(omniauth_info) name = omniauth_info.name.force_encoding("utf-8") email = omniauth_info.email.downcase @@ -105,30 +83,6 @@ class User < ActiveRecord::Base ) end end - - def cared_merge_requests - MergeRequest.where("author_id = :id or assignee_id = :id", :id => self.id).opened - end - - def project_ids - projects.map(&:id) - end - - # Remove user from all projects and - # set blocked attribute to true - def block - users_projects.all.each do |membership| - return false unless membership.destroy - end - - self.blocked = true - save - end - - def projects_limit_percent - return 100 if projects_limit.zero? - (my_own_projects.count.to_f / projects_limit) * 100 - end end # == Schema Information # diff --git a/app/roles/account.rb b/app/roles/account.rb new file mode 100644 index 00000000..62492b83 --- /dev/null +++ b/app/roles/account.rb @@ -0,0 +1,49 @@ +module Account + def identifier + email.gsub /[@.]/, "_" + end + + def is_admin? + admin + end + + def require_ssh_key? + keys.count == 0 + end + + def can_create_project? + projects_limit > my_own_projects.count + end + + def last_activity_project + projects.first + end + + def first_name + name.split.first unless name.blank? + end + + def cared_merge_requests + MergeRequest.where("author_id = :id or assignee_id = :id", :id => self.id).opened + end + + def project_ids + projects.map(&:id) + end + + # Remove user from all projects and + # set blocked attribute to true + def block + users_projects.all.each do |membership| + return false unless membership.destroy + end + + self.blocked = true + save + end + + def projects_limit_percent + return 100 if projects_limit.zero? + (my_own_projects.count.to_f / projects_limit) * 100 + end +end diff --git a/app/roles/authority.rb b/app/roles/authority.rb new file mode 100644 index 00000000..a03b54a3 --- /dev/null +++ b/app/roles/authority.rb @@ -0,0 +1,58 @@ +module Authority + # Compatible with all access rights + # Should be rewrited for new access rights + def add_access(user, *access) + access = if access.include?(:admin) + { :project_access => UsersProject::MASTER } + elsif access.include?(:write) + { :project_access => UsersProject::DEVELOPER } + else + { :project_access => UsersProject::REPORTER } + end + opts = { :user => user } + opts.merge!(access) + users_projects.create(opts) + end + + def reset_access(user) + users_projects.where(:project_id => self.id, :user_id => user.id).destroy if self.id + end + + def repository_readers + keys = Key.joins({:user => :users_projects}). + where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::REPORTER) + keys.map(&:identifier) + deploy_keys.map(&:identifier) + end + + def repository_writers + keys = Key.joins({:user => :users_projects}). + where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::DEVELOPER) + keys.map(&:identifier) + end + + def repository_masters + keys = Key.joins({:user => :users_projects}). + where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::MASTER) + keys.map(&:identifier) + end + + def allow_read_for?(user) + !users_projects.where(:user_id => user.id).empty? + end + + def guest_access_for?(user) + !users_projects.where(:user_id => user.id).empty? + end + + def report_access_for?(user) + !users_projects.where(:user_id => user.id, :project_access => [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty? + end + + def dev_access_for?(user) + !users_projects.where(:user_id => user.id, :project_access => [UsersProject::DEVELOPER, UsersProject::MASTER]).empty? + end + + def master_access_for?(user) + !users_projects.where(:user_id => user.id, :project_access => [UsersProject::MASTER]).empty? || owner_id == user.id + end +end diff --git a/app/roles/git_merge.rb b/app/roles/git_merge.rb new file mode 100644 index 00000000..95e5942f --- /dev/null +++ b/app/roles/git_merge.rb @@ -0,0 +1,2 @@ +module GitMerge +end diff --git a/app/roles/git_push.rb b/app/roles/git_push.rb new file mode 100644 index 00000000..504aa0b5 --- /dev/null +++ b/app/roles/git_push.rb @@ -0,0 +1,107 @@ +module GitPush + def observe_push(oldrev, newrev, ref, user) + data = post_receive_data(oldrev, newrev, ref, user) + + Event.create( + :project => self, + :action => Event::Pushed, + :data => data, + :author_id => data[:user_id] + ) + end + + def update_merge_requests(oldrev, newrev, ref, user) + return true unless ref =~ /heads/ + branch_name = ref.gsub("refs/heads/", "") + c_ids = self.commits_between(oldrev, newrev).map(&:id) + + # Update code for merge requests + mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all + mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } + + # Close merge requests + mrs = self.merge_requests.opened.where(:target_branch => branch_name).all + mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } + mrs.each { |merge_request| merge_request.merge!(user.id) } + + true + end + + def execute_web_hooks(oldrev, newrev, ref, user) + ref_parts = ref.split('/') + + # Return if this is not a push to a branch (e.g. new commits) + return if ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000" + + data = post_receive_data(oldrev, newrev, ref, user) + + web_hooks.each { |web_hook| web_hook.execute(data) } + end + + def post_receive_data(oldrev, newrev, ref, user) + + push_commits = commits_between(oldrev, newrev) + + # Total commits count + push_commits_count = push_commits.size + + # Get latest 20 commits ASC + push_commits_limited = push_commits.last(20) + + # Hash to be passed as post_receive_data + data = { + before: oldrev, + after: newrev, + ref: ref, + user_id: user.id, + user_name: user.name, + repository: { + name: name, + url: web_url, + description: description, + homepage: web_url, + }, + commits: [], + total_commits_count: push_commits_count + } + + # For perfomance purposes maximum 20 latest commits + # will be passed as post receive hook data. + # + push_commits_limited.each do |commit| + data[:commits] << { + id: commit.id, + message: commit.safe_message, + timestamp: commit.date.xmlschema, + url: "http://#{GIT_HOST['host']}/#{code}/commits/#{commit.id}", + author: { + name: commit.author_name, + email: commit.author_email + } + } + end + + data + end + + + # This method will be called after each post receive + # and only if autor_key_id present in gitlab. + # All callbacks for post receive should be placed here + # + def trigger_post_receive(oldrev, newrev, ref, author_key_id) + user = Key.find_by_identifier(author_key_id).user + + # Create push event + self.observe_push(oldrev, newrev, ref, user) + + # Close merged MR + self.update_merge_requests(oldrev, newrev, ref, user) + + # Execute web hooks + self.execute_web_hooks(oldrev, newrev, ref, user) + + # Create satellite + self.satellite.create unless self.satellite.exists? + end +end diff --git a/app/roles/push_event.rb b/app/roles/push_event.rb new file mode 100644 index 00000000..acec916f --- /dev/null +++ b/app/roles/push_event.rb @@ -0,0 +1,90 @@ +module PushEvent + def valid_push? + data[:ref] + rescue => ex + false + end + + def tag? + data[:ref]["refs/tags"] + end + + def new_branch? + commit_from =~ /^00000/ + end + + def new_ref? + commit_from =~ /^00000/ + end + + def rm_ref? + commit_to =~ /^00000/ + end + + def md_ref? + !(rm_ref? || new_ref?) + end + + def commit_from + data[:before] + end + + def commit_to + data[:after] + end + + def ref_name + if tag? + tag_name + else + branch_name + end + end + + def branch_name + @branch_name ||= data[:ref].gsub("refs/heads/", "") + end + + def tag_name + @tag_name ||= data[:ref].gsub("refs/tags/", "") + end + + # Max 20 commits from push DESC + def commits + @commits ||= data[:commits].map { |commit| project.commit(commit[:id]) }.reverse + end + + def commits_count + data[:total_commits_count] || commits.count || 0 + end + + def ref_type + tag? ? "tag" : "branch" + end + + def push_action_name + if new_ref? + "pushed new" + elsif rm_ref? + "removed #{ref_type}" + else + "pushed to" + end + end + + def parent_commit + project.commit(commit_from) + rescue => ex + nil + end + + def last_commit + project.commit(commit_to) + rescue => ex + nil + end + + def push_with_commits? + md_ref? && commits.any? && parent_commit && last_commit + end +end diff --git a/app/roles/repository.rb b/app/roles/repository.rb new file mode 100644 index 00000000..85ed6b7b --- /dev/null +++ b/app/roles/repository.rb @@ -0,0 +1,120 @@ +module Repository + def valid_repo? + repo + rescue + errors.add(:path, "Invalid repository path") + false + end + + def commit(commit_id = nil) + Commit.find_or_first(repo, commit_id, root_ref) + end + + def fresh_commits(n = 10) + Commit.fresh_commits(repo, n) + end + + def commits_with_refs(n = 20) + Commit.commits_with_refs(repo, n) + end + + def commits_since(date) + Commit.commits_since(repo, date) + end + + def commits(ref, path = nil, limit = nil, offset = nil) + Commit.commits(repo, ref, path, limit, offset) + end + + def commits_between(from, to) + Commit.commits_between(repo, from, to) + end + + def write_hooks + %w(post-receive).each do |hook| + write_hook(hook, File.read(File.join(Rails.root, 'lib', "#{hook}-hook"))) + end + end + + def satellite + @satellite ||= Gitlab::Satellite.new(self) + end + + def write_hook(name, content) + hook_file = File.join(path_to_repo, 'hooks', name) + + File.open(hook_file, 'w') do |f| + f.write(content) + end + + File.chmod(0775, hook_file) + end + + def has_post_receive_file? + hook_file = File.join(path_to_repo, 'hooks', 'post-receive') + File.exists?(hook_file) + end + + def tags + repo.tags.map(&:name).sort.reverse + end + + def repo + @repo ||= Grit::Repo.new(path_to_repo) + end + + def url_to_repo + Gitlab::GitHost.url_to_repo(path) + end + + def path_to_repo + File.join(GIT_HOST["base_path"], "#{path}.git") + end + + def update_repository + Gitlab::GitHost.system.update_project(path, self) + + write_hooks if File.exists?(path_to_repo) + end + + def destroy_repository + Gitlab::GitHost.system.destroy_project(self) + end + + def repo_exists? + @repo_exists ||= (repo && !repo.branches.empty?) + rescue + @repo_exists = false + end + + def heads + @heads ||= repo.heads + end + + def tree(fcommit, path = nil) + fcommit = commit if fcommit == :head + tree = fcommit.tree + path ? (tree / path) : tree + end + + def open_branches + if protected_branches.empty? + self.repo.heads + else + pnames = protected_branches.map(&:name) + self.repo.heads.reject { |h| pnames.include?(h.name) } + end.sort_by(&:name) + end + + def has_commits? + !!commit + end + + def root_ref + default_branch || "master" + end + + def root_ref? branch + root_ref == branch + end +end diff --git a/app/roles/ssh_key.rb b/app/roles/ssh_key.rb new file mode 100644 index 00000000..f1143c5d --- /dev/null +++ b/app/roles/ssh_key.rb @@ -0,0 +1,18 @@ +module SshKey + def update_repository + Gitlab::GitHost.system.new.configure do |c| + c.update_keys(identifier, key) + c.update_projects(projects) + end + end + + def repository_delete_key + Gitlab::GitHost.system.new.configure do |c| + #delete key file is there is no identically deploy keys + if !is_deploy_key || Key.where(:identifier => identifier).count() == 0 + c.delete_key(identifier) + end + c.update_projects(projects) + end + end +end diff --git a/app/roles/team.rb b/app/roles/team.rb new file mode 100644 index 00000000..2a477b6e --- /dev/null +++ b/app/roles/team.rb @@ -0,0 +1,10 @@ +module Team + def team_member_by_name_or_email(email = nil, name = nil) + user = users.where("email like ? or name like ?", email, name).first + users_projects.find_by_user_id(user.id) if user + end + + def team_member_by_id(user_id) + users_projects.find_by_user_id(user_id) + end +end diff --git a/app/roles/upvote.rb b/app/roles/upvote.rb new file mode 100644 index 00000000..7efa6f20 --- /dev/null +++ b/app/roles/upvote.rb @@ -0,0 +1,6 @@ +module Upvote + # Return the number of +1 comments (upvotes) + def upvotes + notes.select(&:upvote?).size + end +end