Commit a1999955eb0b212c818a383bc54f8392194d0bc1

Authored by Dmitriy Zaporozhets
2 parents 1b25a8f4 da03a5c7

Merge pull request #2461 from gitlabhq/remove_roles

Remove models/roles and return to fat models
app/models/concerns/issuable.rb 0 → 100644
@@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
  1 +# == Issuable concern
  2 +#
  3 +# Contains common functionality shared between Issues and MergeRequests
  4 +#
  5 +# Used by Issue, MergeRequest
  6 +#
  7 +module Issuable
  8 + extend ActiveSupport::Concern
  9 +
  10 + included do
  11 + belongs_to :project
  12 + belongs_to :author, class_name: "User"
  13 + belongs_to :assignee, class_name: "User"
  14 + belongs_to :milestone
  15 + has_many :notes, as: :noteable, dependent: :destroy
  16 +
  17 + validates :project, presence: true
  18 + validates :author, presence: true
  19 + validates :title, presence: true, length: { within: 0..255 }
  20 + validates :closed, inclusion: { in: [true, false] }
  21 +
  22 + scope :opened, where(closed: false)
  23 + scope :closed, where(closed: true)
  24 + scope :of_group, ->(group) { where(project_id: group.project_ids) }
  25 + scope :assigned, ->(u) { where(assignee_id: u.id)}
  26 + scope :recent, order("created_at DESC")
  27 +
  28 + delegate :name,
  29 + :email,
  30 + to: :author,
  31 + prefix: true
  32 +
  33 + delegate :name,
  34 + :email,
  35 + to: :assignee,
  36 + allow_nil: true,
  37 + prefix: true
  38 +
  39 + attr_accessor :author_id_of_changes
  40 + end
  41 +
  42 + module ClassMethods
  43 + def search(query)
  44 + where("title like :query", query: "%#{query}%")
  45 + end
  46 + end
  47 +
  48 + def today?
  49 + Date.today == created_at.to_date
  50 + end
  51 +
  52 + def new?
  53 + today? && created_at == updated_at
  54 + end
  55 +
  56 + def is_assigned?
  57 + !!assignee_id
  58 + end
  59 +
  60 + def is_being_reassigned?
  61 + assignee_id_changed?
  62 + end
  63 +
  64 + def is_being_closed?
  65 + closed_changed? && closed
  66 + end
  67 +
  68 + def is_being_reopened?
  69 + closed_changed? && !closed
  70 + end
  71 +
  72 + # Return the number of +1 comments (upvotes)
  73 + def upvotes
  74 + notes.select(&:upvote?).size
  75 + end
  76 +
  77 + def upvotes_in_percent
  78 + if votes_count.zero?
  79 + 0
  80 + else
  81 + 100.0 / votes_count * upvotes
  82 + end
  83 + end
  84 +
  85 + # Return the number of -1 comments (downvotes)
  86 + def downvotes
  87 + notes.select(&:downvote?).size
  88 + end
  89 +
  90 + def downvotes_in_percent
  91 + if votes_count.zero?
  92 + 0
  93 + else
  94 + 100.0 - upvotes_in_percent
  95 + end
  96 + end
  97 +
  98 + # Return the total number of votes
  99 + def votes_count
  100 + upvotes + downvotes
  101 + end
  102 +end
app/models/event.rb
@@ -15,9 +15,6 @@ @@ -15,9 +15,6 @@
15 # 15 #
16 16
17 class Event < ActiveRecord::Base 17 class Event < ActiveRecord::Base
18 - include NoteEvent  
19 - include PushEvent  
20 -  
21 attr_accessible :project, :action, :data, :author_id, :project_id, 18 attr_accessible :project, :action, :data, :author_id, :project_id,
22 :target_id, :target_type 19 :target_id, :target_type
23 20
@@ -170,4 +167,139 @@ class Event &lt; ActiveRecord::Base @@ -170,4 +167,139 @@ class Event &lt; ActiveRecord::Base
170 "opened" 167 "opened"
171 end 168 end
172 end 169 end
  170 +
  171 + def valid_push?
  172 + data[:ref]
  173 + rescue => ex
  174 + false
  175 + end
  176 +
  177 + def tag?
  178 + data[:ref]["refs/tags"]
  179 + end
  180 +
  181 + def branch?
  182 + data[:ref]["refs/heads"]
  183 + end
  184 +
  185 + def new_branch?
  186 + commit_from =~ /^00000/
  187 + end
  188 +
  189 + def new_ref?
  190 + commit_from =~ /^00000/
  191 + end
  192 +
  193 + def rm_ref?
  194 + commit_to =~ /^00000/
  195 + end
  196 +
  197 + def md_ref?
  198 + !(rm_ref? || new_ref?)
  199 + end
  200 +
  201 + def commit_from
  202 + data[:before]
  203 + end
  204 +
  205 + def commit_to
  206 + data[:after]
  207 + end
  208 +
  209 + def ref_name
  210 + if tag?
  211 + tag_name
  212 + else
  213 + branch_name
  214 + end
  215 + end
  216 +
  217 + def branch_name
  218 + @branch_name ||= data[:ref].gsub("refs/heads/", "")
  219 + end
  220 +
  221 + def tag_name
  222 + @tag_name ||= data[:ref].gsub("refs/tags/", "")
  223 + end
  224 +
  225 + # Max 20 commits from push DESC
  226 + def commits
  227 + @commits ||= data[:commits].map { |commit| project.commit(commit[:id]) }.reverse
  228 + end
  229 +
  230 + def commits_count
  231 + data[:total_commits_count] || commits.count || 0
  232 + end
  233 +
  234 + def ref_type
  235 + tag? ? "tag" : "branch"
  236 + end
  237 +
  238 + def push_action_name
  239 + if new_ref?
  240 + "pushed new"
  241 + elsif rm_ref?
  242 + "deleted"
  243 + else
  244 + "pushed to"
  245 + end
  246 + end
  247 +
  248 + def parent_commit
  249 + project.commit(commit_from)
  250 + rescue => ex
  251 + nil
  252 + end
  253 +
  254 + def last_commit
  255 + project.commit(commit_to)
  256 + rescue => ex
  257 + nil
  258 + end
  259 +
  260 + def push_with_commits?
  261 + md_ref? && commits.any? && parent_commit && last_commit
  262 + rescue Grit::NoSuchPathError
  263 + false
  264 + end
  265 +
  266 + def last_push_to_non_root?
  267 + branch? && project.default_branch != branch_name
  268 + end
  269 +
  270 + def note_commit_id
  271 + target.commit_id
  272 + end
  273 +
  274 + def note_short_commit_id
  275 + note_commit_id[0..8]
  276 + end
  277 +
  278 + def note_commit?
  279 + target.noteable_type == "Commit"
  280 + end
  281 +
  282 + def note_target
  283 + target.noteable
  284 + end
  285 +
  286 + def note_target_id
  287 + if note_commit?
  288 + target.commit_id
  289 + else
  290 + target.noteable_id.to_s
  291 + end
  292 + end
  293 +
  294 + def wall_note?
  295 + target.noteable_type.blank?
  296 + end
  297 +
  298 + def note_target_type
  299 + if target.noteable_type.present?
  300 + target.noteable_type.titleize
  301 + else
  302 + "Wall"
  303 + end.downcase
  304 + end
173 end 305 end
app/models/issue.rb
@@ -17,8 +17,7 @@ @@ -17,8 +17,7 @@
17 # 17 #
18 18
19 class Issue < ActiveRecord::Base 19 class Issue < ActiveRecord::Base
20 - include IssueCommonality  
21 - include Votes 20 + include Issuable
22 21
23 attr_accessible :title, :assignee_id, :closed, :position, :description, 22 attr_accessible :title, :assignee_id, :closed, :position, :description,
24 :milestone_id, :label_list, :author_id_of_changes 23 :milestone_id, :label_list, :author_id_of_changes
app/models/merge_request.rb
@@ -20,11 +20,10 @@ @@ -20,11 +20,10 @@
20 # 20 #
21 21
22 require Rails.root.join("app/models/commit") 22 require Rails.root.join("app/models/commit")
23 -require Rails.root.join("app/roles/static_model") 23 +require Rails.root.join("lib/static_model")
24 24
25 class MergeRequest < ActiveRecord::Base 25 class MergeRequest < ActiveRecord::Base
26 - include IssueCommonality  
27 - include Votes 26 + include Issuable
28 27
29 attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id, 28 attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id,
30 :author_id_of_changes 29 :author_id_of_changes
app/models/project.rb
@@ -21,11 +21,7 @@ @@ -21,11 +21,7 @@
21 require "grit" 21 require "grit"
22 22
23 class Project < ActiveRecord::Base 23 class Project < ActiveRecord::Base
24 - include Repository  
25 - include PushObserver  
26 - include Authority  
27 - include Team  
28 - include NamespacedProject 24 + include Gitolited
29 25
30 class TransferError < StandardError; end 26 class TransferError < StandardError; end
31 27
@@ -277,4 +273,514 @@ class Project &lt; ActiveRecord::Base @@ -277,4 +273,514 @@ class Project &lt; ActiveRecord::Base
277 creator 273 creator
278 end 274 end
279 end 275 end
  276 +
  277 + def team_member_by_name_or_email(name = nil, email = nil)
  278 + user = users.where("name like ? or email like ?", name, email).first
  279 + users_projects.where(user: user) if user
  280 + end
  281 +
  282 + # Get Team Member record by user id
  283 + def team_member_by_id(user_id)
  284 + users_projects.find_by_user_id(user_id)
  285 + end
  286 +
  287 + # Add user to project
  288 + # with passed access role
  289 + def add_user_to_team(user, access_role)
  290 + add_user_id_to_team(user.id, access_role)
  291 + end
  292 +
  293 + # Add multiple users to project
  294 + # with same access role
  295 + def add_users_to_team(users, access_role)
  296 + add_users_ids_to_team(users.map(&:id), access_role)
  297 + end
  298 +
  299 + # Add user to project
  300 + # with passed access role by user id
  301 + def add_user_id_to_team(user_id, access_role)
  302 + users_projects.create(
  303 + user_id: user_id,
  304 + project_access: access_role
  305 + )
  306 + end
  307 +
  308 + # Add multiple users to project
  309 + # with same access role by user ids
  310 + def add_users_ids_to_team(users_ids, access_role)
  311 + UsersProject.bulk_import(self, users_ids, access_role)
  312 + end
  313 +
  314 + # Update multiple project users
  315 + # to same access role by user ids
  316 + def update_users_ids_to_role(users_ids, access_role)
  317 + UsersProject.bulk_update(self, users_ids, access_role)
  318 + end
  319 +
  320 + # Delete multiple users from project by user ids
  321 + def delete_users_ids_from_team(users_ids)
  322 + UsersProject.bulk_delete(self, users_ids)
  323 + end
  324 +
  325 + # Remove all users from project team
  326 + def truncate_team
  327 + UsersProject.truncate_team(self)
  328 + end
  329 +
  330 + # Compatible with all access rights
  331 + # Should be rewrited for new access rights
  332 + def add_access(user, *access)
  333 + access = if access.include?(:admin)
  334 + { project_access: UsersProject::MASTER }
  335 + elsif access.include?(:write)
  336 + { project_access: UsersProject::DEVELOPER }
  337 + else
  338 + { project_access: UsersProject::REPORTER }
  339 + end
  340 + opts = { user: user }
  341 + opts.merge!(access)
  342 + users_projects.create(opts)
  343 + end
  344 +
  345 + def reset_access(user)
  346 + users_projects.where(project_id: self.id, user_id: user.id).destroy if self.id
  347 + end
  348 +
  349 + def repository_readers
  350 + repository_members[UsersProject::REPORTER]
  351 + end
  352 +
  353 + def repository_writers
  354 + repository_members[UsersProject::DEVELOPER]
  355 + end
  356 +
  357 + def repository_masters
  358 + repository_members[UsersProject::MASTER]
  359 + end
  360 +
  361 + def repository_members
  362 + keys = Hash.new {|h,k| h[k] = [] }
  363 + UsersProject.select("keys.identifier, project_access").
  364 + joins(user: :keys).where(project_id: id).
  365 + each {|row| keys[row.project_access] << [row.identifier] }
  366 +
  367 + keys[UsersProject::REPORTER] += deploy_keys.pluck(:identifier)
  368 + keys
  369 + end
  370 +
  371 + def allow_read_for?(user)
  372 + !users_projects.where(user_id: user.id).empty?
  373 + end
  374 +
  375 + def guest_access_for?(user)
  376 + !users_projects.where(user_id: user.id).empty?
  377 + end
  378 +
  379 + def report_access_for?(user)
  380 + !users_projects.where(user_id: user.id, project_access: [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty?
  381 + end
  382 +
  383 + def dev_access_for?(user)
  384 + !users_projects.where(user_id: user.id, project_access: [UsersProject::DEVELOPER, UsersProject::MASTER]).empty?
  385 + end
  386 +
  387 + def master_access_for?(user)
  388 + !users_projects.where(user_id: user.id, project_access: [UsersProject::MASTER]).empty?
  389 + end
  390 +
  391 + def transfer(new_namespace)
  392 + Project.transaction do
  393 + old_namespace = namespace
  394 + self.namespace = new_namespace
  395 +
  396 + old_dir = old_namespace.try(:path) || ''
  397 + new_dir = new_namespace.try(:path) || ''
  398 +
  399 + old_repo = if old_dir.present?
  400 + File.join(old_dir, self.path)
  401 + else
  402 + self.path
  403 + end
  404 +
  405 + if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present?
  406 + raise TransferError.new("Project with same path in target namespace already exists")
  407 + end
  408 +
  409 + Gitlab::ProjectMover.new(self, old_dir, new_dir).execute
  410 +
  411 + gitolite.move_repository(old_repo, self)
  412 +
  413 + save!
  414 + end
  415 + rescue Gitlab::ProjectMover::ProjectMoveError => ex
  416 + raise Project::TransferError.new(ex.message)
  417 + end
  418 +
  419 + def name_with_namespace
  420 + @name_with_namespace ||= begin
  421 + if namespace
  422 + namespace.human_name + " / " + name
  423 + else
  424 + name
  425 + end
  426 + end
  427 + end
  428 +
  429 + def namespace_owner
  430 + namespace.try(:owner)
  431 + end
  432 +
  433 + def path_with_namespace
  434 + if namespace
  435 + namespace.path + '/' + path
  436 + else
  437 + path
  438 + end
  439 + end
  440 +
  441 + # This method will be called after each post receive and only if the provided
  442 + # user is present in GitLab.
  443 + #
  444 + # All callbacks for post receive should be placed here.
  445 + def trigger_post_receive(oldrev, newrev, ref, user)
  446 + data = post_receive_data(oldrev, newrev, ref, user)
  447 +
  448 + # Create push event
  449 + self.observe_push(data)
  450 +
  451 + if push_to_branch? ref, oldrev
  452 + # Close merged MR
  453 + self.update_merge_requests(oldrev, newrev, ref, user)
  454 +
  455 + # Execute web hooks
  456 + self.execute_hooks(data.dup)
  457 +
  458 + # Execute project services
  459 + self.execute_services(data.dup)
  460 + end
  461 +
  462 + # Create satellite
  463 + self.satellite.create unless self.satellite.exists?
  464 +
  465 + # Discover the default branch, but only if it hasn't already been set to
  466 + # something else
  467 + if default_branch.nil?
  468 + update_attributes(default_branch: discover_default_branch)
  469 + end
  470 + end
  471 +
  472 + def push_to_branch? ref, oldrev
  473 + ref_parts = ref.split('/')
  474 +
  475 + # Return if this is not a push to a branch (e.g. new commits)
  476 + !(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000")
  477 + end
  478 +
  479 + def observe_push(data)
  480 + Event.create(
  481 + project: self,
  482 + action: Event::Pushed,
  483 + data: data,
  484 + author_id: data[:user_id]
  485 + )
  486 + end
  487 +
  488 + def execute_hooks(data)
  489 + hooks.each { |hook| hook.execute(data) }
  490 + end
  491 +
  492 + def execute_services(data)
  493 + services.each do |service|
  494 +
  495 + # Call service hook only if it is active
  496 + service.execute(data) if service.active
  497 + end
  498 + end
  499 +
  500 + # Produce a hash of post-receive data
  501 + #
  502 + # data = {
  503 + # before: String,
  504 + # after: String,
  505 + # ref: String,
  506 + # user_id: String,
  507 + # user_name: String,
  508 + # repository: {
  509 + # name: String,
  510 + # url: String,
  511 + # description: String,
  512 + # homepage: String,
  513 + # },
  514 + # commits: Array,
  515 + # total_commits_count: Fixnum
  516 + # }
  517 + #
  518 + def post_receive_data(oldrev, newrev, ref, user)
  519 +
  520 + push_commits = commits_between(oldrev, newrev)
  521 +
  522 + # Total commits count
  523 + push_commits_count = push_commits.size
  524 +
  525 + # Get latest 20 commits ASC
  526 + push_commits_limited = push_commits.last(20)
  527 +
  528 + # Hash to be passed as post_receive_data
  529 + data = {
  530 + before: oldrev,
  531 + after: newrev,
  532 + ref: ref,
  533 + user_id: user.id,
  534 + user_name: user.name,
  535 + repository: {
  536 + name: name,
  537 + url: url_to_repo,
  538 + description: description,
  539 + homepage: web_url,
  540 + },
  541 + commits: [],
  542 + total_commits_count: push_commits_count
  543 + }
  544 +
  545 + # For perfomance purposes maximum 20 latest commits
  546 + # will be passed as post receive hook data.
  547 + #
  548 + push_commits_limited.each do |commit|
  549 + data[:commits] << {
  550 + id: commit.id,
  551 + message: commit.safe_message,
  552 + timestamp: commit.date.xmlschema,
  553 + url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{commit.id}",
  554 + author: {
  555 + name: commit.author_name,
  556 + email: commit.author_email
  557 + }
  558 + }
  559 + end
  560 +
  561 + data
  562 + end
  563 +
  564 + def update_merge_requests(oldrev, newrev, ref, user)
  565 + return true unless ref =~ /heads/
  566 + branch_name = ref.gsub("refs/heads/", "")
  567 + c_ids = self.commits_between(oldrev, newrev).map(&:id)
  568 +
  569 + # Update code for merge requests
  570 + mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all
  571 + mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
  572 +
  573 + # Close merge requests
  574 + mrs = self.merge_requests.opened.where(target_branch: branch_name).all
  575 + mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) }
  576 + mrs.each { |merge_request| merge_request.merge!(user.id) }
  577 +
  578 + true
  579 + end
  580 +
  581 + def valid_repo?
  582 + repo
  583 + rescue
  584 + errors.add(:path, "Invalid repository path")
  585 + false
  586 + end
  587 +
  588 + def empty_repo?
  589 + !repo_exists? || !has_commits?
  590 + end
  591 +
  592 + def commit(commit_id = nil)
  593 + Commit.find_or_first(repo, commit_id, root_ref)
  594 + end
  595 +
  596 + def fresh_commits(n = 10)
  597 + Commit.fresh_commits(repo, n)
  598 + end
  599 +
  600 + def commits_with_refs(n = 20)
  601 + Commit.commits_with_refs(repo, n)
  602 + end
  603 +
  604 + def commits_since(date)
  605 + Commit.commits_since(repo, date)
  606 + end
  607 +
  608 + def commits(ref, path = nil, limit = nil, offset = nil)
  609 + Commit.commits(repo, ref, path, limit, offset)
  610 + end
  611 +
  612 + def last_commit_for(ref, path = nil)
  613 + commits(ref, path, 1).first
  614 + end
  615 +
  616 + def commits_between(from, to)
  617 + Commit.commits_between(repo, from, to)
  618 + end
  619 +
  620 + def satellite
  621 + @satellite ||= Gitlab::Satellite::Satellite.new(self)
  622 + end
  623 +
  624 + def has_post_receive_file?
  625 + !!hook_file
  626 + end
  627 +
  628 + def valid_post_receive_file?
  629 + valid_hook_file == hook_file
  630 + end
  631 +
  632 + def valid_hook_file
  633 + @valid_hook_file ||= File.read(Rails.root.join('lib', 'hooks', 'post-receive'))
  634 + end
  635 +
  636 + def hook_file
  637 + @hook_file ||= begin
  638 + hook_path = File.join(path_to_repo, 'hooks', 'post-receive')
  639 + File.read(hook_path) if File.exists?(hook_path)
  640 + end
  641 + end
  642 +
  643 + # Returns an Array of branch names
  644 + def branch_names
  645 + repo.branches.collect(&:name).sort
  646 + end
  647 +
  648 + # Returns an Array of Branches
  649 + def branches
  650 + repo.branches.sort_by(&:name)
  651 + end
  652 +
  653 + # Returns an Array of tag names
  654 + def tag_names
  655 + repo.tags.collect(&:name).sort.reverse
  656 + end
  657 +
  658 + # Returns an Array of Tags
  659 + def tags
  660 + repo.tags.sort_by(&:name).reverse
  661 + end
  662 +
  663 + # Returns an Array of branch and tag names
  664 + def ref_names
  665 + [branch_names + tag_names].flatten
  666 + end
  667 +
  668 + def repo
  669 + @repo ||= Grit::Repo.new(path_to_repo)
  670 + end
  671 +
  672 + def url_to_repo
  673 + gitolite.url_to_repo(path_with_namespace)
  674 + end
  675 +
  676 + def path_to_repo
  677 + File.join(Gitlab.config.gitolite.repos_path, "#{path_with_namespace}.git")
  678 + end
  679 +
  680 + def namespace_dir
  681 + namespace.try(:path) || ''
  682 + end
  683 +
  684 + def update_repository
  685 + gitolite.update_repository(self)
  686 + end
  687 +
  688 + def destroy_repository
  689 + gitolite.remove_repository(self)
  690 + end
  691 +
  692 + def repo_exists?
  693 + @repo_exists ||= (repo && !repo.branches.empty?)
  694 + rescue
  695 + @repo_exists = false
  696 + end
  697 +
  698 + def heads
  699 + @heads ||= repo.heads
  700 + end
  701 +
  702 + def tree(fcommit, path = nil)
  703 + fcommit = commit if fcommit == :head
  704 + tree = fcommit.tree
  705 + path ? (tree / path) : tree
  706 + end
  707 +
  708 + def open_branches
  709 + if protected_branches.empty?
  710 + self.repo.heads
  711 + else
  712 + pnames = protected_branches.map(&:name)
  713 + self.repo.heads.reject { |h| pnames.include?(h.name) }
  714 + end.sort_by(&:name)
  715 + end
  716 +
  717 + # Discovers the default branch based on the repository's available branches
  718 + #
  719 + # - If no branches are present, returns nil
  720 + # - If one branch is present, returns its name
  721 + # - If two or more branches are present, returns the one that has a name
  722 + # matching root_ref (default_branch or 'master' if default_branch is nil)
  723 + def discover_default_branch
  724 + if branch_names.length == 0
  725 + nil
  726 + elsif branch_names.length == 1
  727 + branch_names.first
  728 + else
  729 + branch_names.select { |v| v == root_ref }.first
  730 + end
  731 + end
  732 +
  733 + def has_commits?
  734 + !!commit
  735 + rescue Grit::NoSuchPathError
  736 + false
  737 + end
  738 +
  739 + def root_ref
  740 + default_branch || "master"
  741 + end
  742 +
  743 + def root_ref?(branch)
  744 + root_ref == branch
  745 + end
  746 +
  747 + # Archive Project to .tar.gz
  748 + #
  749 + # Already packed repo archives stored at
  750 + # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz
  751 + #
  752 + def archive_repo(ref)
  753 + ref = ref || self.root_ref
  754 + commit = self.commit(ref)
  755 + return nil unless commit
  756 +
  757 + # Build file path
  758 + file_name = self.path + "-" + commit.id.to_s + ".tar.gz"
  759 + storage_path = Rails.root.join("tmp", "repositories", self.path_with_namespace)
  760 + file_path = File.join(storage_path, file_name)
  761 +
  762 + # Put files into a directory before archiving
  763 + prefix = self.path + "/"
  764 +
  765 + # Create file if not exists
  766 + unless File.exists?(file_path)
  767 + FileUtils.mkdir_p storage_path
  768 + file = self.repo.archive_to_file(ref, prefix, file_path)
  769 + end
  770 +
  771 + file_path
  772 + end
  773 +
  774 + def ssh_url_to_repo
  775 + url_to_repo
  776 + end
  777 +
  778 + def http_url_to_repo
  779 + http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
  780 + end
  781 +
  782 + # Check if current branch name is marked as protected in the system
  783 + def protected_branch? branch_name
  784 + protected_branches.map(&:name).include?(branch_name)
  785 + end
280 end 786 end
app/models/protected_branch.rb
@@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
10 # 10 #
11 11
12 class ProtectedBranch < ActiveRecord::Base 12 class ProtectedBranch < ActiveRecord::Base
13 - include GitHost 13 + include Gitolited
14 14
15 attr_accessible :name 15 attr_accessible :name
16 16
@@ -22,7 +22,7 @@ class ProtectedBranch &lt; ActiveRecord::Base @@ -22,7 +22,7 @@ class ProtectedBranch &lt; ActiveRecord::Base
22 after_destroy :update_repository 22 after_destroy :update_repository
23 23
24 def update_repository 24 def update_repository
25 - git_host.update_repository(project) 25 + gitolite.update_repository(project)
26 end 26 end
27 27
28 def commit 28 def commit
app/models/user.rb
@@ -34,8 +34,6 @@ @@ -34,8 +34,6 @@
34 # 34 #
35 35
36 class User < ActiveRecord::Base 36 class User < ActiveRecord::Base
37 - include Account  
38 -  
39 devise :database_authenticatable, :token_authenticatable, :lockable, 37 devise :database_authenticatable, :token_authenticatable, :lockable,
40 :recoverable, :rememberable, :trackable, :validatable, :omniauthable 38 :recoverable, :rememberable, :trackable, :validatable, :omniauthable
41 39
@@ -192,4 +190,92 @@ class User &lt; ActiveRecord::Base @@ -192,4 +190,92 @@ class User &lt; ActiveRecord::Base
192 def tm_in_personal_projects 190 def tm_in_personal_projects
193 personal_projects.users_projects.where(user_id: self.id) 191 personal_projects.users_projects.where(user_id: self.id)
194 end 192 end
  193 +
  194 + # Returns a string for use as a Gitolite user identifier
  195 + #
  196 + # Note that Gitolite 2.x requires the following pattern for users:
  197 + #
  198 + # ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$
  199 + def identifier
  200 + # Replace non-word chars with underscores, then make sure it starts with
  201 + # valid chars
  202 + email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '')
  203 + end
  204 +
  205 + def is_admin?
  206 + admin
  207 + end
  208 +
  209 + def require_ssh_key?
  210 + keys.count == 0
  211 + end
  212 +
  213 + def can_create_project?
  214 + projects_limit > personal_projects.count
  215 + end
  216 +
  217 + def can_create_group?
  218 + is_admin?
  219 + end
  220 +
  221 + def abilities
  222 + @abilities ||= begin
  223 + abilities = Six.new
  224 + abilities << Ability
  225 + abilities
  226 + end
  227 + end
  228 +
  229 + def can? action, subject
  230 + abilities.allowed?(self, action, subject)
  231 + end
  232 +
  233 + def last_activity_project
  234 + projects.first
  235 + end
  236 +
  237 + def first_name
  238 + name.split.first unless name.blank?
  239 + end
  240 +
  241 + def cared_merge_requests
  242 + MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id)
  243 + end
  244 +
  245 + # Remove user from all projects and
  246 + # set blocked attribute to true
  247 + def block
  248 + users_projects.find_each do |membership|
  249 + return false unless membership.destroy
  250 + end
  251 +
  252 + self.blocked = true
  253 + save
  254 + end
  255 +
  256 + def projects_limit_percent
  257 + return 100 if projects_limit.zero?
  258 + (personal_projects.count.to_f / projects_limit) * 100
  259 + end
  260 +
  261 + def recent_push project_id = nil
  262 + # Get push events not earlier than 2 hours ago
  263 + events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours)
  264 + events = events.where(project_id: project_id) if project_id
  265 +
  266 + # Take only latest one
  267 + events = events.recent.limit(1).first
  268 + end
  269 +
  270 + def projects_sorted_by_activity
  271 + authorized_projects.sorted_by_activity
  272 + end
  273 +
  274 + def several_namespaces?
  275 + namespaces.size > 1
  276 + end
  277 +
  278 + def namespace_id
  279 + namespace.try :id
  280 + end
195 end 281 end
app/models/users_project.rb
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 # 11 #
12 12
13 class UsersProject < ActiveRecord::Base 13 class UsersProject < ActiveRecord::Base
14 - include GitHost 14 + include Gitolited
15 15
16 GUEST = 10 16 GUEST = 10
17 REPORTER = 20 17 REPORTER = 20
@@ -152,7 +152,7 @@ class UsersProject &lt; ActiveRecord::Base @@ -152,7 +152,7 @@ class UsersProject &lt; ActiveRecord::Base
152 end 152 end
153 153
154 def update_repository 154 def update_repository
155 - git_host.update_repository(project) 155 + gitolite.update_repository(project)
156 end 156 end
157 157
158 def project_access_human 158 def project_access_human
app/observers/key_observer.rb
1 class KeyObserver < ActiveRecord::Observer 1 class KeyObserver < ActiveRecord::Observer
2 - include GitHost 2 + include Gitolited
3 3
4 def after_save(key) 4 def after_save(key)
5 - git_host.set_key(key.identifier, key.key, key.projects) 5 + gitolite.set_key(key.identifier, key.key, key.projects)
6 end 6 end
7 7
8 def after_destroy(key) 8 def after_destroy(key)
9 return if key.is_deploy_key && !key.last_deploy? 9 return if key.is_deploy_key && !key.last_deploy?
10 - git_host.remove_key(key.identifier, key.projects) 10 + gitolite.remove_key(key.identifier, key.projects)
11 end 11 end
12 end 12 end
app/roles/account.rb
@@ -1,95 +0,0 @@ @@ -1,95 +0,0 @@
1 -# == Account role  
2 -#  
3 -# Describe behaviour of User in application  
4 -#  
5 -# Used by User  
6 -#  
7 -module Account  
8 - # Returns a string for use as a Gitolite user identifier  
9 - #  
10 - # Note that Gitolite 2.x requires the following pattern for users:  
11 - #  
12 - # ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$  
13 - def identifier  
14 - # Replace non-word chars with underscores, then make sure it starts with  
15 - # valid chars  
16 - email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '')  
17 - end  
18 -  
19 - def is_admin?  
20 - admin  
21 - end  
22 -  
23 - def require_ssh_key?  
24 - keys.count == 0  
25 - end  
26 -  
27 - def can_create_project?  
28 - projects_limit > personal_projects.count  
29 - end  
30 -  
31 - def can_create_group?  
32 - is_admin?  
33 - end  
34 -  
35 - def abilities  
36 - @abilities ||= begin  
37 - abilities = Six.new  
38 - abilities << Ability  
39 - abilities  
40 - end  
41 - end  
42 -  
43 - def can? action, subject  
44 - abilities.allowed?(self, action, subject)  
45 - end  
46 -  
47 - def last_activity_project  
48 - projects.first  
49 - end  
50 -  
51 - def first_name  
52 - name.split.first unless name.blank?  
53 - end  
54 -  
55 - def cared_merge_requests  
56 - MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id)  
57 - end  
58 -  
59 - # Remove user from all projects and  
60 - # set blocked attribute to true  
61 - def block  
62 - users_projects.find_each do |membership|  
63 - return false unless membership.destroy  
64 - end  
65 -  
66 - self.blocked = true  
67 - save  
68 - end  
69 -  
70 - def projects_limit_percent  
71 - return 100 if projects_limit.zero?  
72 - (personal_projects.count.to_f / projects_limit) * 100  
73 - end  
74 -  
75 - def recent_push project_id = nil  
76 - # Get push events not earlier than 2 hours ago  
77 - events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours)  
78 - events = events.where(project_id: project_id) if project_id  
79 -  
80 - # Take only latest one  
81 - events = events.recent.limit(1).first  
82 - end  
83 -  
84 - def projects_sorted_by_activity  
85 - authorized_projects.sorted_by_activity  
86 - end  
87 -  
88 - def several_namespaces?  
89 - namespaces.size > 1  
90 - end  
91 -  
92 - def namespace_id  
93 - namespace.try :id  
94 - end  
95 -end  
app/roles/authority.rb
@@ -1,68 +0,0 @@ @@ -1,68 +0,0 @@
1 -# == Authority role  
2 -#  
3 -# Control access to project repository based on users role in team  
4 -#  
5 -# Used by Project  
6 -#  
7 -module Authority  
8 - # Compatible with all access rights  
9 - # Should be rewrited for new access rights  
10 - def add_access(user, *access)  
11 - access = if access.include?(:admin)  
12 - { project_access: UsersProject::MASTER }  
13 - elsif access.include?(:write)  
14 - { project_access: UsersProject::DEVELOPER }  
15 - else  
16 - { project_access: UsersProject::REPORTER }  
17 - end  
18 - opts = { user: user }  
19 - opts.merge!(access)  
20 - users_projects.create(opts)  
21 - end  
22 -  
23 - def reset_access(user)  
24 - users_projects.where(project_id: self.id, user_id: user.id).destroy if self.id  
25 - end  
26 -  
27 - def repository_readers  
28 - repository_members[UsersProject::REPORTER]  
29 - end  
30 -  
31 - def repository_writers  
32 - repository_members[UsersProject::DEVELOPER]  
33 - end  
34 -  
35 - def repository_masters  
36 - repository_members[UsersProject::MASTER]  
37 - end  
38 -  
39 - def repository_members  
40 - keys = Hash.new {|h,k| h[k] = [] }  
41 - UsersProject.select("keys.identifier, project_access").  
42 - joins(user: :keys).where(project_id: id).  
43 - each {|row| keys[row.project_access] << [row.identifier] }  
44 -  
45 - keys[UsersProject::REPORTER] += deploy_keys.pluck(:identifier)  
46 - keys  
47 - end  
48 -  
49 - def allow_read_for?(user)  
50 - !users_projects.where(user_id: user.id).empty?  
51 - end  
52 -  
53 - def guest_access_for?(user)  
54 - !users_projects.where(user_id: user.id).empty?  
55 - end  
56 -  
57 - def report_access_for?(user)  
58 - !users_projects.where(user_id: user.id, project_access: [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty?  
59 - end  
60 -  
61 - def dev_access_for?(user)  
62 - !users_projects.where(user_id: user.id, project_access: [UsersProject::DEVELOPER, UsersProject::MASTER]).empty?  
63 - end  
64 -  
65 - def master_access_for?(user)  
66 - !users_projects.where(user_id: user.id, project_access: [UsersProject::MASTER]).empty?  
67 - end  
68 -end  
app/roles/git_host.rb
@@ -1,11 +0,0 @@ @@ -1,11 +0,0 @@
1 -# == GitHost role  
2 -#  
3 -# Provide a shortcut to Gitlab::Gitolite instance  
4 -#  
5 -# Used by Project, UsersProject  
6 -#  
7 -module GitHost  
8 - def git_host  
9 - Gitlab::Gitolite.new  
10 - end  
11 -end  
app/roles/issue_commonality.rb
@@ -1,72 +0,0 @@ @@ -1,72 +0,0 @@
1 -# == IssueCommonality role  
2 -#  
3 -# Contains common functionality shared between Issues and MergeRequests  
4 -#  
5 -# Used by Issue, MergeRequest  
6 -#  
7 -module IssueCommonality  
8 - extend ActiveSupport::Concern  
9 -  
10 - included do  
11 - belongs_to :project  
12 - belongs_to :author, class_name: "User"  
13 - belongs_to :assignee, class_name: "User"  
14 - belongs_to :milestone  
15 - has_many :notes, as: :noteable, dependent: :destroy  
16 -  
17 - validates :project, presence: true  
18 - validates :author, presence: true  
19 - validates :title, presence: true, length: { within: 0..255 }  
20 - validates :closed, inclusion: { in: [true, false] }  
21 -  
22 - scope :opened, where(closed: false)  
23 - scope :closed, where(closed: true)  
24 - scope :of_group, ->(group) { where(project_id: group.project_ids) }  
25 - scope :assigned, ->(u) { where(assignee_id: u.id)}  
26 - scope :recent, order("created_at DESC")  
27 -  
28 - delegate :name,  
29 - :email,  
30 - to: :author,  
31 - prefix: true  
32 -  
33 - delegate :name,  
34 - :email,  
35 - to: :assignee,  
36 - allow_nil: true,  
37 - prefix: true  
38 -  
39 - attr_accessor :author_id_of_changes  
40 - end  
41 -  
42 - module ClassMethods  
43 - def search(query)  
44 - where("title like :query", query: "%#{query}%")  
45 - end  
46 - end  
47 -  
48 - def today?  
49 - Date.today == created_at.to_date  
50 - end  
51 -  
52 - def new?  
53 - today? && created_at == updated_at  
54 - end  
55 -  
56 - def is_assigned?  
57 - !!assignee_id  
58 - end  
59 -  
60 - def is_being_reassigned?  
61 - assignee_id_changed?  
62 - end  
63 -  
64 - def is_being_closed?  
65 - closed_changed? && closed  
66 - end  
67 -  
68 - def is_being_reopened?  
69 - closed_changed? && !closed  
70 - end  
71 -  
72 -end  
app/roles/namespaced_project.rb
@@ -1,60 +0,0 @@ @@ -1,60 +0,0 @@
1 -# == NamespacedProject role  
2 -#  
3 -# Provides extra functionality for Project related to namespaces like:  
4 -# - transfer project between namespaces  
5 -# - name, path including namespece  
6 -# - project owner based on namespace  
7 -#  
8 -# Used by Project  
9 -#  
10 -module NamespacedProject  
11 - def transfer(new_namespace)  
12 - Project.transaction do  
13 - old_namespace = namespace  
14 - self.namespace = new_namespace  
15 -  
16 - old_dir = old_namespace.try(:path) || ''  
17 - new_dir = new_namespace.try(:path) || ''  
18 -  
19 - old_repo = if old_dir.present?  
20 - File.join(old_dir, self.path)  
21 - else  
22 - self.path  
23 - end  
24 -  
25 - if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present?  
26 - raise TransferError.new("Project with same path in target namespace already exists")  
27 - end  
28 -  
29 - Gitlab::ProjectMover.new(self, old_dir, new_dir).execute  
30 -  
31 - git_host.move_repository(old_repo, self)  
32 -  
33 - save!  
34 - end  
35 - rescue Gitlab::ProjectMover::ProjectMoveError => ex  
36 - raise Project::TransferError.new(ex.message)  
37 - end  
38 -  
39 - def name_with_namespace  
40 - @name_with_namespace ||= begin  
41 - if namespace  
42 - namespace.human_name + " / " + name  
43 - else  
44 - name  
45 - end  
46 - end  
47 - end  
48 -  
49 - def namespace_owner  
50 - namespace.try(:owner)  
51 - end  
52 -  
53 - def path_with_namespace  
54 - if namespace  
55 - namespace.path + '/' + path  
56 - else  
57 - path  
58 - end  
59 - end  
60 -end  
app/roles/note_event.rb
@@ -1,43 +0,0 @@ @@ -1,43 +0,0 @@
1 -# == NoteEvent role  
2 -#  
3 -# Extends Event model functionality by providing extra methods related to comment events  
4 -#  
5 -# Used by Event  
6 -#  
7 -module NoteEvent  
8 - def note_commit_id  
9 - target.commit_id  
10 - end  
11 -  
12 - def note_short_commit_id  
13 - note_commit_id[0..8]  
14 - end  
15 -  
16 - def note_commit?  
17 - target.noteable_type == "Commit"  
18 - end  
19 -  
20 - def note_target  
21 - target.noteable  
22 - end  
23 -  
24 - def note_target_id  
25 - if note_commit?  
26 - target.commit_id  
27 - else  
28 - target.noteable_id.to_s  
29 - end  
30 - end  
31 -  
32 - def wall_note?  
33 - target.noteable_type.blank?  
34 - end  
35 -  
36 - def note_target_type  
37 - if target.noteable_type.present?  
38 - target.noteable_type.titleize  
39 - else  
40 - "Wall"  
41 - end.downcase  
42 - end  
43 -end  
app/roles/push_event.rb
@@ -1,106 +0,0 @@ @@ -1,106 +0,0 @@
1 -# == PushEvent role  
2 -#  
3 -# Extends Event model functionality by providing extra methods related to push events  
4 -#  
5 -# Used by Event  
6 -#  
7 -module PushEvent  
8 - def valid_push?  
9 - data[:ref]  
10 - rescue => ex  
11 - false  
12 - end  
13 -  
14 - def tag?  
15 - data[:ref]["refs/tags"]  
16 - end  
17 -  
18 - def branch?  
19 - data[:ref]["refs/heads"]  
20 - end  
21 -  
22 - def new_branch?  
23 - commit_from =~ /^00000/  
24 - end  
25 -  
26 - def new_ref?  
27 - commit_from =~ /^00000/  
28 - end  
29 -  
30 - def rm_ref?  
31 - commit_to =~ /^00000/  
32 - end  
33 -  
34 - def md_ref?  
35 - !(rm_ref? || new_ref?)  
36 - end  
37 -  
38 - def commit_from  
39 - data[:before]  
40 - end  
41 -  
42 - def commit_to  
43 - data[:after]  
44 - end  
45 -  
46 - def ref_name  
47 - if tag?  
48 - tag_name  
49 - else  
50 - branch_name  
51 - end  
52 - end  
53 -  
54 - def branch_name  
55 - @branch_name ||= data[:ref].gsub("refs/heads/", "")  
56 - end  
57 -  
58 - def tag_name  
59 - @tag_name ||= data[:ref].gsub("refs/tags/", "")  
60 - end  
61 -  
62 - # Max 20 commits from push DESC  
63 - def commits  
64 - @commits ||= data[:commits].map { |commit| project.commit(commit[:id]) }.reverse  
65 - end  
66 -  
67 - def commits_count  
68 - data[:total_commits_count] || commits.count || 0  
69 - end  
70 -  
71 - def ref_type  
72 - tag? ? "tag" : "branch"  
73 - end  
74 -  
75 - def push_action_name  
76 - if new_ref?  
77 - "pushed new"  
78 - elsif rm_ref?  
79 - "deleted"  
80 - else  
81 - "pushed to"  
82 - end  
83 - end  
84 -  
85 - def parent_commit  
86 - project.commit(commit_from)  
87 - rescue => ex  
88 - nil  
89 - end  
90 -  
91 - def last_commit  
92 - project.commit(commit_to)  
93 - rescue => ex  
94 - nil  
95 - end  
96 -  
97 - def push_with_commits?  
98 - md_ref? && commits.any? && parent_commit && last_commit  
99 - rescue Grit::NoSuchPathError  
100 - false  
101 - end  
102 -  
103 - def last_push_to_non_root?  
104 - branch? && project.default_branch != branch_name  
105 - end  
106 -end  
app/roles/push_observer.rb
@@ -1,149 +0,0 @@ @@ -1,149 +0,0 @@
1 -# == PushObserver role  
2 -#  
3 -# Includes methods to be triggered on push to project repository.  
4 -#  
5 -#  
6 -# Used by Project  
7 -# Triggered by PostReceive job  
8 -#  
9 -module PushObserver  
10 - # This method will be called after each post receive and only if the provided  
11 - # user is present in GitLab.  
12 - #  
13 - # All callbacks for post receive should be placed here.  
14 - def trigger_post_receive(oldrev, newrev, ref, user)  
15 - data = post_receive_data(oldrev, newrev, ref, user)  
16 -  
17 - # Create push event  
18 - self.observe_push(data)  
19 -  
20 - if push_to_branch? ref, oldrev  
21 - # Close merged MR  
22 - self.update_merge_requests(oldrev, newrev, ref, user)  
23 -  
24 - # Execute web hooks  
25 - self.execute_hooks(data.dup)  
26 -  
27 - # Execute project services  
28 - self.execute_services(data.dup)  
29 - end  
30 -  
31 - # Create satellite  
32 - self.satellite.create unless self.satellite.exists?  
33 -  
34 - # Discover the default branch, but only if it hasn't already been set to  
35 - # something else  
36 - if default_branch.nil?  
37 - update_attributes(default_branch: discover_default_branch)  
38 - end  
39 - end  
40 -  
41 - def push_to_branch? ref, oldrev  
42 - ref_parts = ref.split('/')  
43 -  
44 - # Return if this is not a push to a branch (e.g. new commits)  
45 - !(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000")  
46 - end  
47 -  
48 - def observe_push(data)  
49 - Event.create(  
50 - project: self,  
51 - action: Event::Pushed,  
52 - data: data,  
53 - author_id: data[:user_id]  
54 - )  
55 - end  
56 -  
57 - def execute_hooks(data)  
58 - hooks.each { |hook| hook.execute(data) }  
59 - end  
60 -  
61 - def execute_services(data)  
62 - services.each do |service|  
63 -  
64 - # Call service hook only if it is active  
65 - service.execute(data) if service.active  
66 - end  
67 - end  
68 -  
69 - # Produce a hash of post-receive data  
70 - #  
71 - # data = {  
72 - # before: String,  
73 - # after: String,  
74 - # ref: String,  
75 - # user_id: String,  
76 - # user_name: String,  
77 - # repository: {  
78 - # name: String,  
79 - # url: String,  
80 - # description: String,  
81 - # homepage: String,  
82 - # },  
83 - # commits: Array,  
84 - # total_commits_count: Fixnum  
85 - # }  
86 - #  
87 - def post_receive_data(oldrev, newrev, ref, user)  
88 -  
89 - push_commits = commits_between(oldrev, newrev)  
90 -  
91 - # Total commits count  
92 - push_commits_count = push_commits.size  
93 -  
94 - # Get latest 20 commits ASC  
95 - push_commits_limited = push_commits.last(20)  
96 -  
97 - # Hash to be passed as post_receive_data  
98 - data = {  
99 - before: oldrev,  
100 - after: newrev,  
101 - ref: ref,  
102 - user_id: user.id,  
103 - user_name: user.name,  
104 - repository: {  
105 - name: name,  
106 - url: url_to_repo,  
107 - description: description,  
108 - homepage: web_url,  
109 - },  
110 - commits: [],  
111 - total_commits_count: push_commits_count  
112 - }  
113 -  
114 - # For perfomance purposes maximum 20 latest commits  
115 - # will be passed as post receive hook data.  
116 - #  
117 - push_commits_limited.each do |commit|  
118 - data[:commits] << {  
119 - id: commit.id,  
120 - message: commit.safe_message,  
121 - timestamp: commit.date.xmlschema,  
122 - url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{commit.id}",  
123 - author: {  
124 - name: commit.author_name,  
125 - email: commit.author_email  
126 - }  
127 - }  
128 - end  
129 -  
130 - data  
131 - end  
132 -  
133 - def update_merge_requests(oldrev, newrev, ref, user)  
134 - return true unless ref =~ /heads/  
135 - branch_name = ref.gsub("refs/heads/", "")  
136 - c_ids = self.commits_between(oldrev, newrev).map(&:id)  
137 -  
138 - # Update code for merge requests  
139 - mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all  
140 - mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }  
141 -  
142 - # Close merge requests  
143 - mrs = self.merge_requests.opened.where(target_branch: branch_name).all  
144 - mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) }  
145 - mrs.each { |merge_request| merge_request.merge!(user.id) }  
146 -  
147 - true  
148 - end  
149 -end  
app/roles/repository.rb
@@ -1,216 +0,0 @@ @@ -1,216 +0,0 @@
1 -# == Repository role  
2 -#  
3 -# Provides access to git repository resources like commits, branches etc..  
4 -# Allows you to manage repository via gitolite interface(git_host)  
5 -#  
6 -# Used by Project  
7 -#  
8 -module Repository  
9 - include GitHost  
10 -  
11 - def valid_repo?  
12 - repo  
13 - rescue  
14 - errors.add(:path, "Invalid repository path")  
15 - false  
16 - end  
17 -  
18 - def empty_repo?  
19 - !repo_exists? || !has_commits?  
20 - end  
21 -  
22 - def commit(commit_id = nil)  
23 - Commit.find_or_first(repo, commit_id, root_ref)  
24 - end  
25 -  
26 - def fresh_commits(n = 10)  
27 - Commit.fresh_commits(repo, n)  
28 - end  
29 -  
30 - def commits_with_refs(n = 20)  
31 - Commit.commits_with_refs(repo, n)  
32 - end  
33 -  
34 - def commits_since(date)  
35 - Commit.commits_since(repo, date)  
36 - end  
37 -  
38 - def commits(ref, path = nil, limit = nil, offset = nil)  
39 - Commit.commits(repo, ref, path, limit, offset)  
40 - end  
41 -  
42 - def last_commit_for(ref, path = nil)  
43 - commits(ref, path, 1).first  
44 - end  
45 -  
46 - def commits_between(from, to)  
47 - Commit.commits_between(repo, from, to)  
48 - end  
49 -  
50 - def satellite  
51 - @satellite ||= Gitlab::Satellite::Satellite.new(self)  
52 - end  
53 -  
54 - def has_post_receive_file?  
55 - !!hook_file  
56 - end  
57 -  
58 - def valid_post_receive_file?  
59 - valid_hook_file == hook_file  
60 - end  
61 -  
62 - def valid_hook_file  
63 - @valid_hook_file ||= File.read(Rails.root.join('lib', 'hooks', 'post-receive'))  
64 - end  
65 -  
66 - def hook_file  
67 - @hook_file ||= begin  
68 - hook_path = File.join(path_to_repo, 'hooks', 'post-receive')  
69 - File.read(hook_path) if File.exists?(hook_path)  
70 - end  
71 - end  
72 -  
73 - # Returns an Array of branch names  
74 - def branch_names  
75 - repo.branches.collect(&:name).sort  
76 - end  
77 -  
78 - # Returns an Array of Branches  
79 - def branches  
80 - repo.branches.sort_by(&:name)  
81 - end  
82 -  
83 - # Returns an Array of tag names  
84 - def tag_names  
85 - repo.tags.collect(&:name).sort.reverse  
86 - end  
87 -  
88 - # Returns an Array of Tags  
89 - def tags  
90 - repo.tags.sort_by(&:name).reverse  
91 - end  
92 -  
93 - # Returns an Array of branch and tag names  
94 - def ref_names  
95 - [branch_names + tag_names].flatten  
96 - end  
97 -  
98 - def repo  
99 - @repo ||= Grit::Repo.new(path_to_repo)  
100 - end  
101 -  
102 - def url_to_repo  
103 - git_host.url_to_repo(path_with_namespace)  
104 - end  
105 -  
106 - def path_to_repo  
107 - File.join(Gitlab.config.gitolite.repos_path, "#{path_with_namespace}.git")  
108 - end  
109 -  
110 - def namespace_dir  
111 - namespace.try(:path) || ''  
112 - end  
113 -  
114 - def update_repository  
115 - git_host.update_repository(self)  
116 - end  
117 -  
118 - def destroy_repository  
119 - git_host.remove_repository(self)  
120 - end  
121 -  
122 - def repo_exists?  
123 - @repo_exists ||= (repo && !repo.branches.empty?)  
124 - rescue  
125 - @repo_exists = false  
126 - end  
127 -  
128 - def heads  
129 - @heads ||= repo.heads  
130 - end  
131 -  
132 - def tree(fcommit, path = nil)  
133 - fcommit = commit if fcommit == :head  
134 - tree = fcommit.tree  
135 - path ? (tree / path) : tree  
136 - end  
137 -  
138 - def open_branches  
139 - if protected_branches.empty?  
140 - self.repo.heads  
141 - else  
142 - pnames = protected_branches.map(&:name)  
143 - self.repo.heads.reject { |h| pnames.include?(h.name) }  
144 - end.sort_by(&:name)  
145 - end  
146 -  
147 - # Discovers the default branch based on the repository's available branches  
148 - #  
149 - # - If no branches are present, returns nil  
150 - # - If one branch is present, returns its name  
151 - # - If two or more branches are present, returns the one that has a name  
152 - # matching root_ref (default_branch or 'master' if default_branch is nil)  
153 - def discover_default_branch  
154 - if branch_names.length == 0  
155 - nil  
156 - elsif branch_names.length == 1  
157 - branch_names.first  
158 - else  
159 - branch_names.select { |v| v == root_ref }.first  
160 - end  
161 - end  
162 -  
163 - def has_commits?  
164 - !!commit  
165 - rescue Grit::NoSuchPathError  
166 - false  
167 - end  
168 -  
169 - def root_ref  
170 - default_branch || "master"  
171 - end  
172 -  
173 - def root_ref?(branch)  
174 - root_ref == branch  
175 - end  
176 -  
177 - # Archive Project to .tar.gz  
178 - #  
179 - # Already packed repo archives stored at  
180 - # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz  
181 - #  
182 - def archive_repo(ref)  
183 - ref = ref || self.root_ref  
184 - commit = self.commit(ref)  
185 - return nil unless commit  
186 -  
187 - # Build file path  
188 - file_name = self.path + "-" + commit.id.to_s + ".tar.gz"  
189 - storage_path = Rails.root.join("tmp", "repositories", self.path_with_namespace)  
190 - file_path = File.join(storage_path, file_name)  
191 -  
192 - # Put files into a directory before archiving  
193 - prefix = self.path + "/"  
194 -  
195 - # Create file if not exists  
196 - unless File.exists?(file_path)  
197 - FileUtils.mkdir_p storage_path  
198 - file = self.repo.archive_to_file(ref, prefix, file_path)  
199 - end  
200 -  
201 - file_path  
202 - end  
203 -  
204 - def ssh_url_to_repo  
205 - url_to_repo  
206 - end  
207 -  
208 - def http_url_to_repo  
209 - http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')  
210 - end  
211 -  
212 - # Check if current branch name is marked as protected in the system  
213 - def protected_branch? branch_name  
214 - protected_branches.map(&:name).include?(branch_name)  
215 - end  
216 -end  
app/roles/static_model.rb
@@ -1,47 +0,0 @@ @@ -1,47 +0,0 @@
1 -# Provides an ActiveRecord-like interface to a model whose data is not persisted to a database.  
2 -module StaticModel  
3 - extend ActiveSupport::Concern  
4 -  
5 - module ClassMethods  
6 - # Used by ActiveRecord's polymorphic association to set object_id  
7 - def primary_key  
8 - 'id'  
9 - end  
10 -  
11 - # Used by ActiveRecord's polymorphic association to set object_type  
12 - def base_class  
13 - self  
14 - end  
15 - end  
16 -  
17 - # Used by AR for fetching attributes  
18 - #  
19 - # Pass it along if we respond to it.  
20 - def [](key)  
21 - send(key) if respond_to?(key)  
22 - end  
23 -  
24 - def to_param  
25 - id  
26 - end  
27 -  
28 - def new_record?  
29 - false  
30 - end  
31 -  
32 - def persisted?  
33 - false  
34 - end  
35 -  
36 - def destroyed?  
37 - false  
38 - end  
39 -  
40 - def ==(other)  
41 - if other.is_a? StaticModel  
42 - id == other.id  
43 - else  
44 - super  
45 - end  
46 - end  
47 -end  
app/roles/team.rb
@@ -1,63 +0,0 @@ @@ -1,63 +0,0 @@
1 -# == Team role  
2 -#  
3 -# Provides functionality to manage project team  
4 -# - add user/users to project  
5 -# - update existing membership  
6 -# - remove users from project team  
7 -#  
8 -# Used by Project  
9 -#  
10 -module Team  
11 - def team_member_by_name_or_email(name = nil, email = nil)  
12 - user = users.where("name like ? or email like ?", name, email).first  
13 - users_projects.where(user: user) if user  
14 - end  
15 -  
16 - # Get Team Member record by user id  
17 - def team_member_by_id(user_id)  
18 - users_projects.find_by_user_id(user_id)  
19 - end  
20 -  
21 - # Add user to project  
22 - # with passed access role  
23 - def add_user_to_team(user, access_role)  
24 - add_user_id_to_team(user.id, access_role)  
25 - end  
26 -  
27 - # Add multiple users to project  
28 - # with same access role  
29 - def add_users_to_team(users, access_role)  
30 - add_users_ids_to_team(users.map(&:id), access_role)  
31 - end  
32 -  
33 - # Add user to project  
34 - # with passed access role by user id  
35 - def add_user_id_to_team(user_id, access_role)  
36 - users_projects.create(  
37 - user_id: user_id,  
38 - project_access: access_role  
39 - )  
40 - end  
41 -  
42 - # Add multiple users to project  
43 - # with same access role by user ids  
44 - def add_users_ids_to_team(users_ids, access_role)  
45 - UsersProject.bulk_import(self, users_ids, access_role)  
46 - end  
47 -  
48 - # Update multiple project users  
49 - # to same access role by user ids  
50 - def update_users_ids_to_role(users_ids, access_role)  
51 - UsersProject.bulk_update(self, users_ids, access_role)  
52 - end  
53 -  
54 - # Delete multiple users from project by user ids  
55 - def delete_users_ids_from_team(users_ids)  
56 - UsersProject.bulk_delete(self, users_ids)  
57 - end  
58 -  
59 - # Remove all users from project team  
60 - def truncate_team  
61 - UsersProject.truncate_team(self)  
62 - end  
63 -end  
app/roles/votes.rb
@@ -1,39 +0,0 @@ @@ -1,39 +0,0 @@
1 -# == Votes role  
2 -#  
3 -# Provides functionality to upvote/downvote entity  
4 -# based on +1 and -1 notes  
5 -#  
6 -# Used for Issue and Merge Request  
7 -#  
8 -module Votes  
9 - # Return the number of +1 comments (upvotes)  
10 - def upvotes  
11 - notes.select(&:upvote?).size  
12 - end  
13 -  
14 - def upvotes_in_percent  
15 - if votes_count.zero?  
16 - 0  
17 - else  
18 - 100.0 / votes_count * upvotes  
19 - end  
20 - end  
21 -  
22 - # Return the number of -1 comments (downvotes)  
23 - def downvotes  
24 - notes.select(&:downvote?).size  
25 - end  
26 -  
27 - def downvotes_in_percent  
28 - if votes_count.zero?  
29 - 0  
30 - else  
31 - 100.0 - upvotes_in_percent  
32 - end  
33 - end  
34 -  
35 - # Return the total number of votes  
36 - def votes_count  
37 - upvotes + downvotes  
38 - end  
39 -end  
lib/gitolited.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +# == Gitolited mixin
  2 +#
  3 +# Provide a shortcut to Gitlab::Gitolite instance by gitolite
  4 +#
  5 +# Used by Project, UsersProject, etc
  6 +#
  7 +module Gitolited
  8 + def gitolite
  9 + Gitlab::Gitolite.new
  10 + end
  11 +end
lib/static_model.rb 0 → 100644
@@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
  1 +# Provides an ActiveRecord-like interface to a model whose data is not persisted to a database.
  2 +module StaticModel
  3 + extend ActiveSupport::Concern
  4 +
  5 + module ClassMethods
  6 + # Used by ActiveRecord's polymorphic association to set object_id
  7 + def primary_key
  8 + 'id'
  9 + end
  10 +
  11 + # Used by ActiveRecord's polymorphic association to set object_type
  12 + def base_class
  13 + self
  14 + end
  15 + end
  16 +
  17 + # Used by AR for fetching attributes
  18 + #
  19 + # Pass it along if we respond to it.
  20 + def [](key)
  21 + send(key) if respond_to?(key)
  22 + end
  23 +
  24 + def to_param
  25 + id
  26 + end
  27 +
  28 + def new_record?
  29 + false
  30 + end
  31 +
  32 + def persisted?
  33 + false
  34 + end
  35 +
  36 + def destroyed?
  37 + false
  38 + end
  39 +
  40 + def ==(other)
  41 + if other.is_a? StaticModel
  42 + id == other.id
  43 + else
  44 + super
  45 + end
  46 + end
  47 +end
spec/lib/issue_commonality_spec.rb 0 → 100644
@@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Issue, "IssueCommonality" do
  4 + let(:issue) { create(:issue) }
  5 +
  6 + describe "Associations" do
  7 + it { should belong_to(:project) }
  8 + it { should belong_to(:author) }
  9 + it { should belong_to(:assignee) }
  10 + it { should have_many(:notes).dependent(:destroy) }
  11 + end
  12 +
  13 + describe "Validation" do
  14 + it { should validate_presence_of(:project) }
  15 + it { should validate_presence_of(:author) }
  16 + it { should validate_presence_of(:title) }
  17 + it { should ensure_length_of(:title).is_at_least(0).is_at_most(255) }
  18 + it { should ensure_inclusion_of(:closed).in_array([true, false]) }
  19 + end
  20 +
  21 + describe "Scope" do
  22 + it { described_class.should respond_to(:opened) }
  23 + it { described_class.should respond_to(:closed) }
  24 + it { described_class.should respond_to(:assigned) }
  25 + end
  26 +
  27 + it "has an :author_id_of_changes accessor" do
  28 + issue.should respond_to(:author_id_of_changes)
  29 + issue.should respond_to(:author_id_of_changes=)
  30 + end
  31 +
  32 + describe ".search" do
  33 + let!(:searchable_issue) { create(:issue, title: "Searchable issue") }
  34 +
  35 + it "matches by title" do
  36 + described_class.search('able').all.should == [searchable_issue]
  37 + end
  38 + end
  39 +
  40 + describe "#today?" do
  41 + it "returns true when created today" do
  42 + # Avoid timezone differences and just return exactly what we want
  43 + Date.stub(:today).and_return(issue.created_at.to_date)
  44 + issue.today?.should be_true
  45 + end
  46 +
  47 + it "returns false when not created today" do
  48 + Date.stub(:today).and_return(Date.yesterday)
  49 + issue.today?.should be_false
  50 + end
  51 + end
  52 +
  53 + describe "#new?" do
  54 + it "returns true when created today and record hasn't been updated" do
  55 + issue.stub(:today?).and_return(true)
  56 + issue.new?.should be_true
  57 + end
  58 +
  59 + it "returns false when not created today" do
  60 + issue.stub(:today?).and_return(false)
  61 + issue.new?.should be_false
  62 + end
  63 +
  64 + it "returns false when record has been updated" do
  65 + issue.stub(:today?).and_return(true)
  66 + issue.touch
  67 + issue.new?.should be_false
  68 + end
  69 + end
  70 +end
spec/lib/votes_spec.rb 0 → 100644
@@ -0,0 +1,132 @@ @@ -0,0 +1,132 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Issue do
  4 + let(:issue) { create(:issue) }
  5 +
  6 + describe "#upvotes" do
  7 + it "with no notes has a 0/0 score" do
  8 + issue.upvotes.should == 0
  9 + end
  10 +
  11 + it "should recognize non-+1 notes" do
  12 + issue.notes << create(:note, note: "No +1 here")
  13 + issue.should have(1).note
  14 + issue.notes.first.upvote?.should be_false
  15 + issue.upvotes.should == 0
  16 + end
  17 +
  18 + it "should recognize a single +1 note" do
  19 + issue.notes << create(:note, note: "+1 This is awesome")
  20 + issue.upvotes.should == 1
  21 + end
  22 +
  23 + it "should recognize multiple +1 notes" do
  24 + issue.notes << create(:note, note: "+1 This is awesome")
  25 + issue.notes << create(:note, note: "+1 I want this")
  26 + issue.upvotes.should == 2
  27 + end
  28 + end
  29 +
  30 + describe "#downvotes" do
  31 + it "with no notes has a 0/0 score" do
  32 + issue.downvotes.should == 0
  33 + end
  34 +
  35 + it "should recognize non--1 notes" do
  36 + issue.notes << create(:note, note: "Almost got a -1")
  37 + issue.should have(1).note
  38 + issue.notes.first.downvote?.should be_false
  39 + issue.downvotes.should == 0
  40 + end
  41 +
  42 + it "should recognize a single -1 note" do
  43 + issue.notes << create(:note, note: "-1 This is bad")
  44 + issue.downvotes.should == 1
  45 + end
  46 +
  47 + it "should recognize multiple -1 notes" do
  48 + issue.notes << create(:note, note: "-1 This is bad")
  49 + issue.notes << create(:note, note: "-1 Away with this")
  50 + issue.downvotes.should == 2
  51 + end
  52 + end
  53 +
  54 + describe "#votes_count" do
  55 + it "with no notes has a 0/0 score" do
  56 + issue.votes_count.should == 0
  57 + end
  58 +
  59 + it "should recognize non notes" do
  60 + issue.notes << create(:note, note: "No +1 here")
  61 + issue.should have(1).note
  62 + issue.votes_count.should == 0
  63 + end
  64 +
  65 + it "should recognize a single +1 note" do
  66 + issue.notes << create(:note, note: "+1 This is awesome")
  67 + issue.votes_count.should == 1
  68 + end
  69 +
  70 + it "should recognize a single -1 note" do
  71 + issue.notes << create(:note, note: "-1 This is bad")
  72 + issue.votes_count.should == 1
  73 + end
  74 +
  75 + it "should recognize multiple notes" do
  76 + issue.notes << create(:note, note: "+1 This is awesome")
  77 + issue.notes << create(:note, note: "-1 This is bad")
  78 + issue.notes << create(:note, note: "+1 I want this")
  79 + issue.votes_count.should == 3
  80 + end
  81 + end
  82 +
  83 + describe "#upvotes_in_percent" do
  84 + it "with no notes has a 0% score" do
  85 + issue.upvotes_in_percent.should == 0
  86 + end
  87 +
  88 + it "should count a single 1 note as 100%" do
  89 + issue.notes << create(:note, note: "+1 This is awesome")
  90 + issue.upvotes_in_percent.should == 100
  91 + end
  92 +
  93 + it "should count multiple +1 notes as 100%" do
  94 + issue.notes << create(:note, note: "+1 This is awesome")
  95 + issue.notes << create(:note, note: "+1 I want this")
  96 + issue.upvotes_in_percent.should == 100
  97 + end
  98 +
  99 + it "should count fractions for multiple +1 and -1 notes correctly" do
  100 + issue.notes << create(:note, note: "+1 This is awesome")
  101 + issue.notes << create(:note, note: "+1 I want this")
  102 + issue.notes << create(:note, note: "-1 This is bad")
  103 + issue.notes << create(:note, note: "+1 me too")
  104 + issue.upvotes_in_percent.should == 75
  105 + end
  106 + end
  107 +
  108 + describe "#downvotes_in_percent" do
  109 + it "with no notes has a 0% score" do
  110 + issue.downvotes_in_percent.should == 0
  111 + end
  112 +
  113 + it "should count a single -1 note as 100%" do
  114 + issue.notes << create(:note, note: "-1 This is bad")
  115 + issue.downvotes_in_percent.should == 100
  116 + end
  117 +
  118 + it "should count multiple -1 notes as 100%" do
  119 + issue.notes << create(:note, note: "-1 This is bad")
  120 + issue.notes << create(:note, note: "-1 Away with this")
  121 + issue.downvotes_in_percent.should == 100
  122 + end
  123 +
  124 + it "should count fractions for multiple +1 and -1 notes correctly" do
  125 + issue.notes << create(:note, note: "+1 This is awesome")
  126 + issue.notes << create(:note, note: "+1 I want this")
  127 + issue.notes << create(:note, note: "-1 This is bad")
  128 + issue.notes << create(:note, note: "+1 me too")
  129 + issue.downvotes_in_percent.should == 25
  130 + end
  131 + end
  132 +end
spec/models/project_repository_spec.rb 0 → 100644
@@ -0,0 +1,159 @@ @@ -0,0 +1,159 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Project, "Repository" do
  4 + let(:project) { create(:project) }
  5 +
  6 + describe "#empty_repo?" do
  7 + it "should return true if the repo doesn't exist" do
  8 + project.stub(repo_exists?: false, has_commits?: true)
  9 + project.should be_empty_repo
  10 + end
  11 +
  12 + it "should return true if the repo has commits" do
  13 + project.stub(repo_exists?: true, has_commits?: false)
  14 + project.should be_empty_repo
  15 + end
  16 +
  17 + it "should return false if the repo exists and has commits" do
  18 + project.stub(repo_exists?: true, has_commits?: true)
  19 + project.should_not be_empty_repo
  20 + end
  21 + end
  22 +
  23 + describe "#discover_default_branch" do
  24 + let(:master) { 'master' }
  25 + let(:stable) { 'stable' }
  26 +
  27 + it "returns 'master' when master exists" do
  28 + project.should_receive(:branch_names).at_least(:once).and_return([stable, master])
  29 + project.discover_default_branch.should == 'master'
  30 + end
  31 +
  32 + it "returns non-master when master exists but default branch is set to something else" do
  33 + project.default_branch = 'stable'
  34 + project.should_receive(:branch_names).at_least(:once).and_return([stable, master])
  35 + project.discover_default_branch.should == 'stable'
  36 + end
  37 +
  38 + it "returns a non-master branch when only one exists" do
  39 + project.should_receive(:branch_names).at_least(:once).and_return([stable])
  40 + project.discover_default_branch.should == 'stable'
  41 + end
  42 +
  43 + it "returns nil when no branch exists" do
  44 + project.should_receive(:branch_names).at_least(:once).and_return([])
  45 + project.discover_default_branch.should be_nil
  46 + end
  47 + end
  48 +
  49 + describe "#root_ref" do
  50 + it "returns default_branch when set" do
  51 + project.default_branch = 'stable'
  52 + project.root_ref.should == 'stable'
  53 + end
  54 +
  55 + it "returns 'master' when default_branch is nil" do
  56 + project.default_branch = nil
  57 + project.root_ref.should == 'master'
  58 + end
  59 + end
  60 +
  61 + describe "#root_ref?" do
  62 + it "returns true when branch is root_ref" do
  63 + project.default_branch = 'stable'
  64 + project.root_ref?('stable').should be_true
  65 + end
  66 +
  67 + it "returns false when branch is not root_ref" do
  68 + project.default_branch = nil
  69 + project.root_ref?('stable').should be_false
  70 + end
  71 + end
  72 +
  73 + describe :repo do
  74 + it "should return valid repo" do
  75 + project.repo.should be_kind_of(Grit::Repo)
  76 + end
  77 +
  78 + it "should return nil" do
  79 + lambda { Project.new(path: "invalid").repo }.should raise_error(Grit::NoSuchPathError)
  80 + end
  81 +
  82 + it "should return nil" do
  83 + lambda { Project.new.repo }.should raise_error(TypeError)
  84 + end
  85 + end
  86 +
  87 + describe :commit do
  88 + it "should return first head commit if without params" do
  89 + project.commit.id.should == project.repo.commits.first.id
  90 + end
  91 +
  92 + it "should return valid commit" do
  93 + project.commit(ValidCommit::ID).should be_valid_commit
  94 + end
  95 +
  96 + it "should return nil" do
  97 + project.commit("+123_4532530XYZ").should be_nil
  98 + end
  99 + end
  100 +
  101 + describe :tree do
  102 + before do
  103 + @commit = project.commit(ValidCommit::ID)
  104 + end
  105 +
  106 + it "should raise error w/o arguments" do
  107 + lambda { project.tree }.should raise_error
  108 + end
  109 +
  110 + it "should return root tree for commit" do
  111 + tree = project.tree(@commit)
  112 + tree.contents.size.should == ValidCommit::FILES_COUNT
  113 + tree.contents.map(&:name).should == ValidCommit::FILES
  114 + end
  115 +
  116 + it "should return root tree for commit with correct path" do
  117 + tree = project.tree(@commit, ValidCommit::C_FILE_PATH)
  118 + tree.contents.map(&:name).should == ValidCommit::C_FILES
  119 + end
  120 +
  121 + it "should return root tree for commit with incorrect path" do
  122 + project.tree(@commit, "invalid_path").should be_nil
  123 + end
  124 + end
  125 +
  126 + describe "fresh commits" do
  127 + let(:project) { create(:project) }
  128 +
  129 + it { project.fresh_commits(3).count.should == 3 }
  130 + it { project.fresh_commits.first.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" }
  131 + it { project.fresh_commits.last.id.should == "f403da73f5e62794a0447aca879360494b08f678" }
  132 + end
  133 +
  134 + describe "commits_between" do
  135 + let(:project) { create(:project) }
  136 +
  137 + subject do
  138 + commits = project.commits_between("3a4b4fb4cde7809f033822a171b9feae19d41fff",
  139 + "8470d70da67355c9c009e4401746b1d5410af2e3")
  140 + commits.map { |c| c.id }
  141 + end
  142 +
  143 + it { should have(3).elements }
  144 + it { should include("f0f14c8eaba69ebddd766498a9d0b0e79becd633") }
  145 + it { should_not include("bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a") }
  146 + end
  147 +
  148 + describe :valid_repo? do
  149 + it "should be valid repo" do
  150 + project = create(:project)
  151 + project.valid_repo?.should be_true
  152 + end
  153 +
  154 + it "should be invalid repo" do
  155 + project = Project.new(name: "ok_name", path: "/INVALID_PATH/", path: "NEOK")
  156 + project.valid_repo?.should be_false
  157 + end
  158 + end
  159 +end
spec/models/project_spec.rb
@@ -133,14 +133,6 @@ describe Project do @@ -133,14 +133,6 @@ describe Project do
133 it { should respond_to(:path_with_namespace) } 133 it { should respond_to(:path_with_namespace) }
134 end 134 end
135 135
136 - describe 'modules' do  
137 - it { should include_module(Repository) }  
138 - it { should include_module(PushObserver) }  
139 - it { should include_module(Authority) }  
140 - it { should include_module(Team) }  
141 - it { should include_module(NamespacedProject) }  
142 - end  
143 -  
144 it "should return valid url to repo" do 136 it "should return valid url to repo" do
145 project = Project.new(path: "somewhere") 137 project = Project.new(path: "somewhere")
146 project.url_to_repo.should == Gitlab.config.gitolite.ssh_path_prefix + "somewhere.git" 138 project.url_to_repo.should == Gitlab.config.gitolite.ssh_path_prefix + "somewhere.git"
spec/models/user_spec.rb
@@ -65,10 +65,6 @@ describe User do @@ -65,10 +65,6 @@ describe User do
65 it { should ensure_length_of(:bio).is_within(0..255) } 65 it { should ensure_length_of(:bio).is_within(0..255) }
66 end 66 end
67 67
68 - describe 'modules' do  
69 - it { should include_module(Account) }  
70 - end  
71 -  
72 describe "Respond to" do 68 describe "Respond to" do
73 it { should respond_to(:is_admin?) } 69 it { should respond_to(:is_admin?) }
74 it { should respond_to(:identifier) } 70 it { should respond_to(:identifier) }
@@ -185,4 +181,14 @@ describe User do @@ -185,4 +181,14 @@ describe User do
185 181
186 it { User.not_in_project(@project).should == [@user, @project.owner] } 182 it { User.not_in_project(@project).should == [@user, @project.owner] }
187 end 183 end
  184 +
  185 + describe 'normal user' do
  186 + let(:user) { create(:user, name: 'John Smith') }
  187 +
  188 + it { user.is_admin?.should be_false }
  189 + it { user.require_ssh_key?.should be_true }
  190 + it { user.can_create_group?.should be_false }
  191 + it { user.can_create_project?.should be_true }
  192 + it { user.first_name.should == 'John' }
  193 + end
188 end 194 end
spec/roles/account_role_spec.rb
@@ -1,13 +0,0 @@ @@ -1,13 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe User, "Account" do  
4 - describe 'normal user' do  
5 - let(:user) { create(:user, name: 'John Smith') }  
6 -  
7 - it { user.is_admin?.should be_false }  
8 - it { user.require_ssh_key?.should be_true }  
9 - it { user.can_create_group?.should be_false }  
10 - it { user.can_create_project?.should be_true }  
11 - it { user.first_name.should == 'John' }  
12 - end  
13 -end  
spec/roles/issue_commonality_spec.rb
@@ -1,70 +0,0 @@ @@ -1,70 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe Issue, "IssueCommonality" do  
4 - let(:issue) { create(:issue) }  
5 -  
6 - describe "Associations" do  
7 - it { should belong_to(:project) }  
8 - it { should belong_to(:author) }  
9 - it { should belong_to(:assignee) }  
10 - it { should have_many(:notes).dependent(:destroy) }  
11 - end  
12 -  
13 - describe "Validation" do  
14 - it { should validate_presence_of(:project) }  
15 - it { should validate_presence_of(:author) }  
16 - it { should validate_presence_of(:title) }  
17 - it { should ensure_length_of(:title).is_at_least(0).is_at_most(255) }  
18 - it { should ensure_inclusion_of(:closed).in_array([true, false]) }  
19 - end  
20 -  
21 - describe "Scope" do  
22 - it { described_class.should respond_to(:opened) }  
23 - it { described_class.should respond_to(:closed) }  
24 - it { described_class.should respond_to(:assigned) }  
25 - end  
26 -  
27 - it "has an :author_id_of_changes accessor" do  
28 - issue.should respond_to(:author_id_of_changes)  
29 - issue.should respond_to(:author_id_of_changes=)  
30 - end  
31 -  
32 - describe ".search" do  
33 - let!(:searchable_issue) { create(:issue, title: "Searchable issue") }  
34 -  
35 - it "matches by title" do  
36 - described_class.search('able').all.should == [searchable_issue]  
37 - end  
38 - end  
39 -  
40 - describe "#today?" do  
41 - it "returns true when created today" do  
42 - # Avoid timezone differences and just return exactly what we want  
43 - Date.stub(:today).and_return(issue.created_at.to_date)  
44 - issue.today?.should be_true  
45 - end  
46 -  
47 - it "returns false when not created today" do  
48 - Date.stub(:today).and_return(Date.yesterday)  
49 - issue.today?.should be_false  
50 - end  
51 - end  
52 -  
53 - describe "#new?" do  
54 - it "returns true when created today and record hasn't been updated" do  
55 - issue.stub(:today?).and_return(true)  
56 - issue.new?.should be_true  
57 - end  
58 -  
59 - it "returns false when not created today" do  
60 - issue.stub(:today?).and_return(false)  
61 - issue.new?.should be_false  
62 - end  
63 -  
64 - it "returns false when record has been updated" do  
65 - issue.stub(:today?).and_return(true)  
66 - issue.touch  
67 - issue.new?.should be_false  
68 - end  
69 - end  
70 -end  
spec/roles/repository_spec.rb
@@ -1,159 +0,0 @@ @@ -1,159 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe Project, "Repository" do  
4 - let(:project) { create(:project) }  
5 -  
6 - describe "#empty_repo?" do  
7 - it "should return true if the repo doesn't exist" do  
8 - project.stub(repo_exists?: false, has_commits?: true)  
9 - project.should be_empty_repo  
10 - end  
11 -  
12 - it "should return true if the repo has commits" do  
13 - project.stub(repo_exists?: true, has_commits?: false)  
14 - project.should be_empty_repo  
15 - end  
16 -  
17 - it "should return false if the repo exists and has commits" do  
18 - project.stub(repo_exists?: true, has_commits?: true)  
19 - project.should_not be_empty_repo  
20 - end  
21 - end  
22 -  
23 - describe "#discover_default_branch" do  
24 - let(:master) { 'master' }  
25 - let(:stable) { 'stable' }  
26 -  
27 - it "returns 'master' when master exists" do  
28 - project.should_receive(:branch_names).at_least(:once).and_return([stable, master])  
29 - project.discover_default_branch.should == 'master'  
30 - end  
31 -  
32 - it "returns non-master when master exists but default branch is set to something else" do  
33 - project.default_branch = 'stable'  
34 - project.should_receive(:branch_names).at_least(:once).and_return([stable, master])  
35 - project.discover_default_branch.should == 'stable'  
36 - end  
37 -  
38 - it "returns a non-master branch when only one exists" do  
39 - project.should_receive(:branch_names).at_least(:once).and_return([stable])  
40 - project.discover_default_branch.should == 'stable'  
41 - end  
42 -  
43 - it "returns nil when no branch exists" do  
44 - project.should_receive(:branch_names).at_least(:once).and_return([])  
45 - project.discover_default_branch.should be_nil  
46 - end  
47 - end  
48 -  
49 - describe "#root_ref" do  
50 - it "returns default_branch when set" do  
51 - project.default_branch = 'stable'  
52 - project.root_ref.should == 'stable'  
53 - end  
54 -  
55 - it "returns 'master' when default_branch is nil" do  
56 - project.default_branch = nil  
57 - project.root_ref.should == 'master'  
58 - end  
59 - end  
60 -  
61 - describe "#root_ref?" do  
62 - it "returns true when branch is root_ref" do  
63 - project.default_branch = 'stable'  
64 - project.root_ref?('stable').should be_true  
65 - end  
66 -  
67 - it "returns false when branch is not root_ref" do  
68 - project.default_branch = nil  
69 - project.root_ref?('stable').should be_false  
70 - end  
71 - end  
72 -  
73 - describe :repo do  
74 - it "should return valid repo" do  
75 - project.repo.should be_kind_of(Grit::Repo)  
76 - end  
77 -  
78 - it "should return nil" do  
79 - lambda { Project.new(path: "invalid").repo }.should raise_error(Grit::NoSuchPathError)  
80 - end  
81 -  
82 - it "should return nil" do  
83 - lambda { Project.new.repo }.should raise_error(TypeError)  
84 - end  
85 - end  
86 -  
87 - describe :commit do  
88 - it "should return first head commit if without params" do  
89 - project.commit.id.should == project.repo.commits.first.id  
90 - end  
91 -  
92 - it "should return valid commit" do  
93 - project.commit(ValidCommit::ID).should be_valid_commit  
94 - end  
95 -  
96 - it "should return nil" do  
97 - project.commit("+123_4532530XYZ").should be_nil  
98 - end  
99 - end  
100 -  
101 - describe :tree do  
102 - before do  
103 - @commit = project.commit(ValidCommit::ID)  
104 - end  
105 -  
106 - it "should raise error w/o arguments" do  
107 - lambda { project.tree }.should raise_error  
108 - end  
109 -  
110 - it "should return root tree for commit" do  
111 - tree = project.tree(@commit)  
112 - tree.contents.size.should == ValidCommit::FILES_COUNT  
113 - tree.contents.map(&:name).should == ValidCommit::FILES  
114 - end  
115 -  
116 - it "should return root tree for commit with correct path" do  
117 - tree = project.tree(@commit, ValidCommit::C_FILE_PATH)  
118 - tree.contents.map(&:name).should == ValidCommit::C_FILES  
119 - end  
120 -  
121 - it "should return root tree for commit with incorrect path" do  
122 - project.tree(@commit, "invalid_path").should be_nil  
123 - end  
124 - end  
125 -  
126 - describe "fresh commits" do  
127 - let(:project) { create(:project) }  
128 -  
129 - it { project.fresh_commits(3).count.should == 3 }  
130 - it { project.fresh_commits.first.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" }  
131 - it { project.fresh_commits.last.id.should == "f403da73f5e62794a0447aca879360494b08f678" }  
132 - end  
133 -  
134 - describe "commits_between" do  
135 - let(:project) { create(:project) }  
136 -  
137 - subject do  
138 - commits = project.commits_between("3a4b4fb4cde7809f033822a171b9feae19d41fff",  
139 - "8470d70da67355c9c009e4401746b1d5410af2e3")  
140 - commits.map { |c| c.id }  
141 - end  
142 -  
143 - it { should have(3).elements }  
144 - it { should include("f0f14c8eaba69ebddd766498a9d0b0e79becd633") }  
145 - it { should_not include("bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a") }  
146 - end  
147 -  
148 - describe :valid_repo? do  
149 - it "should be valid repo" do  
150 - project = create(:project)  
151 - project.valid_repo?.should be_true  
152 - end  
153 -  
154 - it "should be invalid repo" do  
155 - project = Project.new(name: "ok_name", path: "/INVALID_PATH/", path: "NEOK")  
156 - project.valid_repo?.should be_false  
157 - end  
158 - end  
159 -end  
spec/roles/votes_spec.rb
@@ -1,132 +0,0 @@ @@ -1,132 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe Issue do  
4 - let(:issue) { create(:issue) }  
5 -  
6 - describe "#upvotes" do  
7 - it "with no notes has a 0/0 score" do  
8 - issue.upvotes.should == 0  
9 - end  
10 -  
11 - it "should recognize non-+1 notes" do  
12 - issue.notes << create(:note, note: "No +1 here")  
13 - issue.should have(1).note  
14 - issue.notes.first.upvote?.should be_false  
15 - issue.upvotes.should == 0  
16 - end  
17 -  
18 - it "should recognize a single +1 note" do  
19 - issue.notes << create(:note, note: "+1 This is awesome")  
20 - issue.upvotes.should == 1  
21 - end  
22 -  
23 - it "should recognize multiple +1 notes" do  
24 - issue.notes << create(:note, note: "+1 This is awesome")  
25 - issue.notes << create(:note, note: "+1 I want this")  
26 - issue.upvotes.should == 2  
27 - end  
28 - end  
29 -  
30 - describe "#downvotes" do  
31 - it "with no notes has a 0/0 score" do  
32 - issue.downvotes.should == 0  
33 - end  
34 -  
35 - it "should recognize non--1 notes" do  
36 - issue.notes << create(:note, note: "Almost got a -1")  
37 - issue.should have(1).note  
38 - issue.notes.first.downvote?.should be_false  
39 - issue.downvotes.should == 0  
40 - end  
41 -  
42 - it "should recognize a single -1 note" do  
43 - issue.notes << create(:note, note: "-1 This is bad")  
44 - issue.downvotes.should == 1  
45 - end  
46 -  
47 - it "should recognize multiple -1 notes" do  
48 - issue.notes << create(:note, note: "-1 This is bad")  
49 - issue.notes << create(:note, note: "-1 Away with this")  
50 - issue.downvotes.should == 2  
51 - end  
52 - end  
53 -  
54 - describe "#votes_count" do  
55 - it "with no notes has a 0/0 score" do  
56 - issue.votes_count.should == 0  
57 - end  
58 -  
59 - it "should recognize non notes" do  
60 - issue.notes << create(:note, note: "No +1 here")  
61 - issue.should have(1).note  
62 - issue.votes_count.should == 0  
63 - end  
64 -  
65 - it "should recognize a single +1 note" do  
66 - issue.notes << create(:note, note: "+1 This is awesome")  
67 - issue.votes_count.should == 1  
68 - end  
69 -  
70 - it "should recognize a single -1 note" do  
71 - issue.notes << create(:note, note: "-1 This is bad")  
72 - issue.votes_count.should == 1  
73 - end  
74 -  
75 - it "should recognize multiple notes" do  
76 - issue.notes << create(:note, note: "+1 This is awesome")  
77 - issue.notes << create(:note, note: "-1 This is bad")  
78 - issue.notes << create(:note, note: "+1 I want this")  
79 - issue.votes_count.should == 3  
80 - end  
81 - end  
82 -  
83 - describe "#upvotes_in_percent" do  
84 - it "with no notes has a 0% score" do  
85 - issue.upvotes_in_percent.should == 0  
86 - end  
87 -  
88 - it "should count a single 1 note as 100%" do  
89 - issue.notes << create(:note, note: "+1 This is awesome")  
90 - issue.upvotes_in_percent.should == 100  
91 - end  
92 -  
93 - it "should count multiple +1 notes as 100%" do  
94 - issue.notes << create(:note, note: "+1 This is awesome")  
95 - issue.notes << create(:note, note: "+1 I want this")  
96 - issue.upvotes_in_percent.should == 100  
97 - end  
98 -  
99 - it "should count fractions for multiple +1 and -1 notes correctly" do  
100 - issue.notes << create(:note, note: "+1 This is awesome")  
101 - issue.notes << create(:note, note: "+1 I want this")  
102 - issue.notes << create(:note, note: "-1 This is bad")  
103 - issue.notes << create(:note, note: "+1 me too")  
104 - issue.upvotes_in_percent.should == 75  
105 - end  
106 - end  
107 -  
108 - describe "#downvotes_in_percent" do  
109 - it "with no notes has a 0% score" do  
110 - issue.downvotes_in_percent.should == 0  
111 - end  
112 -  
113 - it "should count a single -1 note as 100%" do  
114 - issue.notes << create(:note, note: "-1 This is bad")  
115 - issue.downvotes_in_percent.should == 100  
116 - end  
117 -  
118 - it "should count multiple -1 notes as 100%" do  
119 - issue.notes << create(:note, note: "-1 This is bad")  
120 - issue.notes << create(:note, note: "-1 Away with this")  
121 - issue.downvotes_in_percent.should == 100  
122 - end  
123 -  
124 - it "should count fractions for multiple +1 and -1 notes correctly" do  
125 - issue.notes << create(:note, note: "+1 This is awesome")  
126 - issue.notes << create(:note, note: "+1 I want this")  
127 - issue.notes << create(:note, note: "-1 This is bad")  
128 - issue.notes << create(:note, note: "+1 me too")  
129 - issue.downvotes_in_percent.should == 25  
130 - end  
131 - end  
132 -end  
spec/support/stubbed_repository.rb
1 # Stubs out all Git repository access done by models so that specs can run 1 # Stubs out all Git repository access done by models so that specs can run
2 # against fake repositories without Grit complaining that they don't exist. 2 # against fake repositories without Grit complaining that they don't exist.
3 -module StubbedRepository 3 +class Project
4 def path_to_repo 4 def path_to_repo
5 if new_record? || path == 'newproject' 5 if new_record? || path == 'newproject'
6 # There are a couple Project specs and features that expect the Project's 6 # There are a couple Project specs and features that expect the Project's
@@ -27,5 +27,3 @@ module StubbedRepository @@ -27,5 +27,3 @@ module StubbedRepository
27 end 27 end
28 end 28 end
29 end 29 end
30 -  
31 -Project.send(:include, StubbedRepository)