diff options
author | Alfredo Sumaran <alfredo@gitlab.com> | 2016-02-22 21:15:40 -0500 |
---|---|---|
committer | Alfredo Sumaran <alfredo@gitlab.com> | 2016-02-22 21:15:40 -0500 |
commit | 67948368aec144ff7e9997ce184cb4d111622f08 (patch) | |
tree | c94e382fb1aca074b6ce503255564828cb387e36 /app/models | |
parent | 608012629cc90337aba6c5bd4b2b73b530091fb0 (diff) | |
parent | 7c8099853621cc2a244bf6e0fd37a8b503d1ec5e (diff) | |
download | gitlab-ce-sidebar-sizing-higher-viewport.tar.gz |
Merge branch 'master' into sidebar-sizing-higher-viewportsidebar-sizing-higher-viewport
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/blob.rb | 34 | ||||
-rw-r--r-- | app/models/ci/build.rb | 52 | ||||
-rw-r--r-- | app/models/ci/runner.rb | 6 | ||||
-rw-r--r-- | app/models/commit.rb | 38 | ||||
-rw-r--r-- | app/models/commit_status.rb | 16 | ||||
-rw-r--r-- | app/models/concerns/issuable.rb | 25 | ||||
-rw-r--r-- | app/models/label.rb | 19 | ||||
-rw-r--r-- | app/models/merge_request.rb | 11 | ||||
-rw-r--r-- | app/models/milestone.rb | 14 | ||||
-rw-r--r-- | app/models/note.rb | 5 | ||||
-rw-r--r-- | app/models/project.rb | 4 | ||||
-rw-r--r-- | app/models/project_team.rb | 2 | ||||
-rw-r--r-- | app/models/repository.rb | 83 | ||||
-rw-r--r-- | app/models/todo.rb | 53 | ||||
-rw-r--r-- | app/models/user.rb | 2 |
15 files changed, 327 insertions, 37 deletions
diff --git a/app/models/blob.rb b/app/models/blob.rb new file mode 100644 index 00000000000..8ee9f3006b2 --- /dev/null +++ b/app/models/blob.rb @@ -0,0 +1,34 @@ +# Blob is a Rails-specific wrapper around Gitlab::Git::Blob objects +class Blob < SimpleDelegator + # Wrap a Gitlab::Git::Blob object, or return nil when given nil + # + # This method prevents the decorated object from evaluating to "truthy" when + # given a nil value. For example: + # + # blob = Blob.new(nil) + # puts "truthy" if blob # => "truthy" + # + # blob = Blob.decorate(nil) + # puts "truthy" if blob # No output + def self.decorate(blob) + return if blob.nil? + + new(blob) + end + + def svg? + text? && language && language.name == 'SVG' + end + + def to_partial_path + if lfs_pointer? + 'download' + elsif image? || svg? + 'image' + elsif text? + 'text' + else + 'download' + end + end +end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 623edd8bc57..1227458e525 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -31,15 +31,19 @@ # artifacts_file :text # gl_project_id :integer # artifacts_metadata :text +# erased_by_id :integer +# erased_at :datetime # module Ci class Build < CommitStatus include Gitlab::Application.routes.url_helpers + LAZY_ATTRIBUTES = ['trace'] belongs_to :runner, class_name: 'Ci::Runner' belongs_to :trigger_request, class_name: 'Ci::TriggerRequest' + belongs_to :erased_by, class_name: 'User' serialize :options @@ -103,23 +107,22 @@ module Ci end state_machine :status, initial: :pending do - after_transition pending: :running do |build, transition| + after_transition pending: :running do |build| build.execute_hooks end - after_transition any => [:success, :failed, :canceled] do |build, transition| - return unless build.project + # We use around_transition to create builds for next stage as soon as possible, before the `after_*` is executed + around_transition any => [:success, :failed, :canceled] do |build, block| + block.call + build.commit.create_next_builds(build) if build.commit + end + after_transition any => [:success, :failed, :canceled] do |build| build.update_coverage - build.commit.create_next_builds(build) build.execute_hooks end end - def ignored? - failed? && allow_failure? - end - def retryable? project.builds_enabled? && commands.present? end @@ -179,6 +182,7 @@ module Ci end def update_coverage + return unless project coverage_regex = project.build_coverage_regex return unless coverage_regex coverage = extract_coverage(trace, coverage_regex) @@ -203,6 +207,10 @@ module Ci end end + def has_trace? + raw_trace.present? + end + def raw_trace if File.file?(path_to_trace) File.read(path_to_trace) @@ -330,6 +338,7 @@ module Ci end def execute_hooks + return unless project build_data = Gitlab::BuildDataBuilder.build(self) project.execute_hooks(build_data.dup, :build_hooks) project.execute_services(build_data.dup, :build_hooks) @@ -359,6 +368,33 @@ module Ci Gitlab::Ci::Build::Artifacts::Metadata.new(artifacts_metadata.path, path, **options).to_entry end + def erase(opts = {}) + return false unless erasable? + + remove_artifacts_file! + remove_artifacts_metadata! + erase_trace! + update_erased!(opts[:erased_by]) + end + + def erasable? + complete? && (artifacts? || has_trace?) + end + + def erased? + !self.erased_at.nil? + end + + private + + def erase_trace! + self.trace = nil + end + + def update_erased!(user = nil) + self.update(erased_by: user, erased_at: Time.now) + end + private def yaml_variables diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 38b20cd7faa..e725a6d468c 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -22,6 +22,7 @@ module Ci extend Ci::Model LAST_CONTACT_TIME = 5.minutes.ago + AVAILABLE_SCOPES = ['specific', 'shared', 'active', 'paused', 'online'] has_many :builds, class_name: 'Ci::Build' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' @@ -38,6 +39,11 @@ module Ci scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) } scope :ordered, ->() { order(id: :desc) } + scope :owned_or_shared, ->(project_id) do + joins('LEFT JOIN ci_runner_projects ON ci_runner_projects.runner_id = ci_runners.id') + .where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id) + end + acts_as_taggable def self.search(query) diff --git a/app/models/commit.rb b/app/models/commit.rb index 23b771aebb7..3224f5457f0 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -215,6 +215,44 @@ class Commit ci_commit.try(:status) || :not_found end + def revert_branch_name + "revert-#{short_id}" + end + + def revert_description + if merged_merge_request + "This reverts merge request #{merged_merge_request.to_reference}" + else + "This reverts commit #{sha}" + end + end + + def revert_message + %Q{Revert "#{title}"\n\n#{revert_description}} + end + + def reverts_commit?(commit) + description? && description.include?(commit.revert_description) + end + + def merge_commit? + parents.size > 1 + end + + def merged_merge_request + return @merged_merge_request if defined?(@merged_merge_request) + + @merged_merge_request = project.merge_requests.find_by(merge_commit_sha: id) if merge_commit? + end + + def has_been_reverted?(current_user = nil, noteable = self) + Gitlab::ReferenceExtractor.lazily do + noteable.notes.system.flat_map do |note| + note.all_references(current_user).commits + end + end.any? { |commit_ref| commit_ref.reverts_commit?(self) } + end + private def repo_changes diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 66e0502fc0c..7ef50836322 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -75,16 +75,16 @@ class CommitStatus < ActiveRecord::Base transition [:pending, :running] => :canceled end - after_transition pending: :running do |build, transition| - build.update_attributes started_at: Time.now + after_transition pending: :running do |commit_status| + commit_status.update_attributes started_at: Time.now end - after_transition any => [:success, :failed, :canceled] do |build, transition| - build.update_attributes finished_at: Time.now + after_transition any => [:success, :failed, :canceled] do |commit_status| + commit_status.update_attributes finished_at: Time.now end - after_transition [:pending, :running] => :success do |build, transition| - MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.project, nil).trigger(build) + after_transition [:pending, :running] => :success do |commit_status| + MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.commit.project, nil).trigger(commit_status) end state :pending, value: 'pending' @@ -113,6 +113,10 @@ class CommitStatus < ActiveRecord::Base canceled? || success? || failed? end + def ignored? + failed? && allow_failure? + end + def duration if started_at && finished_at finished_at - started_at diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 5136d0196a5..e5f089fb8a0 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -69,10 +69,35 @@ module Issuable case method.to_s when 'milestone_due_asc' then order_milestone_due_asc when 'milestone_due_desc' then order_milestone_due_desc + when 'downvotes_desc' then order_downvotes_desc + when 'upvotes_desc' then order_upvotes_desc else order_by(method) end end + + def order_downvotes_desc + order_votes_desc('thumbsdown') + end + + def order_upvotes_desc + order_votes_desc('thumbsup') + end + + def order_votes_desc(award_emoji_name) + issuable_table = self.arel_table + note_table = Note.arel_table + + join_clause = issuable_table.join(note_table, Arel::Nodes::OuterJoin).on( + note_table[:noteable_id].eq(issuable_table[:id]).and( + note_table[:noteable_type].eq(self.name).and( + note_table[:is_award].eq(true).and(note_table[:note].eq(award_emoji_name)) + ) + ) + ).join_sources + + joins(join_clause).group(issuable_table[:id]).reorder("COUNT(notes.id) DESC") + end end def today? diff --git a/app/models/label.rb b/app/models/label.rb index 220da10a6ab..07a1db4abe5 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -2,13 +2,14 @@ # # Table name: labels # -# id :integer not null, primary key -# title :string(255) -# color :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# template :boolean default(FALSE) +# id :integer not null, primary key +# title :string(255) +# color :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# template :boolean default(FALSE) +# description :string(255) # class Label < ActiveRecord::Base @@ -85,6 +86,10 @@ class Label < ActiveRecord::Base issues.opened.count end + def closed_issues_count + issues.closed.count + end + def template? template end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 1be8061e53d..1543ef311d7 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -24,6 +24,7 @@ # merge_params :text # merge_when_build_succeeds :boolean default(FALSE), not null # merge_user_id :integer +# merge_commit_sha :string # require Rails.root.join("app/models/commit") @@ -137,7 +138,7 @@ class MergeRequest < ActiveRecord::Base scope :by_milestone, ->(milestone) { where(milestone_id: milestone) } scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) } scope :of_projects, ->(ids) { where(target_project_id: ids) } - scope :opened, -> { with_state(:opened) } + scope :opened, -> { with_states(:opened, :reopened) } scope :merged, -> { with_state(:merged) } scope :closed, -> { with_state(:closed) } scope :closed_and_merged, -> { with_states(:closed, :merged) } @@ -532,4 +533,12 @@ class MergeRequest < ActiveRecord::Base [diff_base_commit, last_commit] end + + def merge_commit + @merge_commit ||= project.commit(merge_commit_sha) if merge_commit_sha + end + + def can_be_reverted?(current_user = nil) + merge_commit && !merge_commit.has_been_reverted?(current_user, self) + end end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 9c4476c768e..cbe65d70997 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -27,6 +27,7 @@ class Milestone < ActiveRecord::Base belongs_to :project has_many :issues + has_many :labels, through: :issues has_many :merge_requests has_many :participants, through: :issues, source: :assignee @@ -109,6 +110,19 @@ class Milestone < ActiveRecord::Base 0 end + # Returns the elapsed time (in percent) since the Milestone creation date until today. + # If the Milestone doesn't have a due_date then returns 0 since we can't calculate the elapsed time. + # If the Milestone is overdue then it returns 100%. + def percent_time_used + return 0 unless due_date + return 100 if expired? + + duration = ((created_at - due_date.to_datetime) / 1.day) + days_elapsed = ((created_at - Time.now) / 1.day) + + ((days_elapsed.to_f / duration) * 100).floor + end + def expires_at if due_date if due_date.past? diff --git a/app/models/note.rb b/app/models/note.rb index 55255d22c2f..d287e0f3c6d 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -37,6 +37,8 @@ class Note < ActiveRecord::Base belongs_to :author, class_name: "User" belongs_to :updated_by, class_name: "User" + has_many :todos, dependent: :destroy + delegate :name, to: :project, prefix: true delegate :name, :email, to: :author, prefix: true @@ -375,6 +377,7 @@ class Note < ActiveRecord::Base # def set_award! return unless awards_supported? && contains_emoji_only? + self.is_award = true self.note = award_emoji_name end @@ -382,7 +385,7 @@ class Note < ActiveRecord::Base private def awards_supported? - noteable.kind_of?(Issue) || noteable.is_a?(MergeRequest) + (noteable.kind_of?(Issue) || noteable.is_a?(MergeRequest)) && !for_diff_line? end def contains_emoji_only? diff --git a/app/models/project.rb b/app/models/project.rb index a43878ebcad..95ad88c76ae 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -382,6 +382,10 @@ class Project < ActiveRecord::Base external_import? || forked? end + def no_import? + import_status == 'none' + end + def external_import? import_url.present? end diff --git a/app/models/project_team.rb b/app/models/project_team.rb index 9f380a382cb..9629c7e1bb9 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -136,7 +136,7 @@ class ProjectTeam end def human_max_access(user_id) - Gitlab::Access.options.key max_member_access(user_id) + Gitlab::Access.options_with_owner.key(max_member_access(user_id)) end # This method assumes project and group members are eager loaded for optimal diff --git a/app/models/repository.rb b/app/models/repository.rb index ba275fd9803..e050bd45254 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -23,13 +23,11 @@ class Repository def raw_repository return nil unless path_with_namespace - @raw_repository ||= begin - repo = Gitlab::Git::Repository.new(path_to_repo) - repo.autocrlf = :input - repo - rescue Gitlab::Git::Repository::NoRepository - nil - end + @raw_repository ||= Gitlab::Git::Repository.new(path_to_repo) + end + + def update_autocrlf_option + raw_repository.autocrlf = :input if raw_repository.autocrlf != :input end # Return absolute path to repository @@ -40,7 +38,12 @@ class Repository end def exists? - raw_repository + return false unless raw_repository + + raw_repository.rugged + true + rescue Gitlab::Git::Repository::NoRepository + false end def empty? @@ -67,7 +70,7 @@ class Repository end def commit(id = 'HEAD') - return nil unless raw_repository + return nil unless exists? commit = Gitlab::Git::Commit.find(raw_repository, id) commit = Commit.new(commit, @project) if commit commit @@ -236,6 +239,19 @@ class Repository end expire_branch_cache(branch_name) + + # This ensures this particular cache is flushed after the first commit to a + # new repository. + expire_emptiness_caches if empty? + end + + # Expires _all_ caches, including those that would normally only be expired + # under specific conditions. + def expire_all_caches! + expire_cache + expire_root_ref_cache + expire_emptiness_caches + expire_has_visible_content_cache end def expire_branch_cache(branch_name = nil) @@ -258,6 +274,14 @@ class Repository @root_ref = nil end + # Expires the cache(s) used to determine if a repository is empty or not. + def expire_emptiness_caches + cache.expire(:empty?) + @empty = nil + + expire_has_visible_content_cache + end + def expire_has_visible_content_cache cache.expire(:has_visible_content?) @has_visible_content = nil @@ -599,6 +623,34 @@ class Repository end end + def revert(user, commit, base_branch, target_branch = nil) + source_sha = find_branch(base_branch).target + target_branch ||= base_branch + args = [commit.id, source_sha] + args << { mainline: 1 } if commit.merge_commit? + + revert_index = rugged.revert_commit(*args) + return false if revert_index.conflicts? + + tree_id = revert_index.write_tree(rugged) + return false unless diff_exists?(source_sha, tree_id) + + commit_with_hooks(user, target_branch) do |ref| + committer = user_to_committer(user) + source_sha = Rugged::Commit.create(rugged, + message: commit.revert_message, + author: committer, + committer: committer, + tree: tree_id, + parents: [rugged.lookup(source_sha)], + update_ref: ref) + end + end + + def diff_exists?(sha1, sha2) + rugged.diff(sha1, sha2).size > 0 + end + def merged_to_root_ref?(branch_name) branch_commit = commit(branch_name) root_ref_commit = commit(root_ref) @@ -611,6 +663,8 @@ class Repository end def merge_base(first_commit_id, second_commit_id) + first_commit_id = commit(first_commit_id).try(:id) || first_commit_id + second_commit_id = commit(second_commit_id).try(:id) || second_commit_id rugged.merge_base(first_commit_id, second_commit_id) rescue Rugged::ReferenceError nil @@ -674,12 +728,15 @@ class Repository end def commit_with_hooks(current_user, branch) + update_autocrlf_option + oldrev = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + branch + target_branch = find_branch(branch) was_empty = empty? - unless was_empty - oldrev = find_branch(branch).target + if !was_empty && target_branch + oldrev = target_branch.target end with_tmp_ref(oldrev) do |tmp_ref| @@ -691,7 +748,7 @@ class Repository end GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do - if was_empty + if was_empty || !target_branch # Create branch rugged.references.create(ref, newrev) else @@ -706,6 +763,8 @@ class Repository end end end + + newrev end end diff --git a/app/models/todo.rb b/app/models/todo.rb new file mode 100644 index 00000000000..34d71c1b0d3 --- /dev/null +++ b/app/models/todo.rb @@ -0,0 +1,53 @@ +# == Schema Information +# +# Table name: todos +# +# id :integer not null, primary key +# user_id :integer not null +# project_id :integer not null +# target_id :integer not null +# target_type :string not null +# author_id :integer +# note_id :integer +# action :integer not null +# state :string not null +# created_at :datetime +# updated_at :datetime +# + +class Todo < ActiveRecord::Base + ASSIGNED = 1 + MENTIONED = 2 + + belongs_to :author, class_name: "User" + belongs_to :note + belongs_to :project + belongs_to :target, polymorphic: true, touch: true + belongs_to :user + + delegate :name, :email, to: :author, prefix: true, allow_nil: true + + validates :action, :project, :target, :user, presence: true + + default_scope { reorder(id: :desc) } + + scope :pending, -> { with_state(:pending) } + scope :done, -> { with_state(:done) } + + state_machine :state, initial: :pending do + event :done do + transition pending: :done + end + + state :pending + state :done + end + + def body + if note.present? + note.note + else + target.title + end + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 9fe94b13e52..02ff2456f2b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -140,7 +140,7 @@ class User < ActiveRecord::Base has_one :abuse_report, dependent: :destroy has_many :spam_logs, dependent: :destroy has_many :builds, dependent: :nullify, class_name: 'Ci::Build' - + has_many :todos, dependent: :destroy # # Validations |