diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/blob.rb | 46 | ||||
-rw-r--r-- | app/models/blob_viewer/base.rb | 11 | ||||
-rw-r--r-- | app/models/ci/artifact_blob.rb | 35 | ||||
-rw-r--r-- | app/models/commit.rb | 4 | ||||
-rw-r--r-- | app/models/concerns/blob_like.rb | 48 | ||||
-rw-r--r-- | app/models/concerns/cache_markdown_field.rb | 3 | ||||
-rw-r--r-- | app/models/concerns/discussion_on_diff.rb | 1 | ||||
-rw-r--r-- | app/models/concerns/note_on_diff.rb | 4 | ||||
-rw-r--r-- | app/models/diff_discussion.rb | 20 | ||||
-rw-r--r-- | app/models/diff_note.rb | 7 | ||||
-rw-r--r-- | app/models/issue.rb | 8 | ||||
-rw-r--r-- | app/models/legacy_diff_discussion.rb | 18 | ||||
-rw-r--r-- | app/models/merge_request.rb | 21 | ||||
-rw-r--r-- | app/models/namespace.rb | 6 | ||||
-rw-r--r-- | app/models/note.rb | 22 | ||||
-rw-r--r-- | app/models/project.rb | 30 | ||||
-rw-r--r-- | app/models/project_services/chat_message/base_message.rb | 11 | ||||
-rw-r--r-- | app/models/project_services/chat_message/pipeline_message.rb | 10 | ||||
-rw-r--r-- | app/models/repository.rb | 50 | ||||
-rw-r--r-- | app/models/snippet_blob.rb | 30 | ||||
-rw-r--r-- | app/models/user.rb | 2 |
21 files changed, 276 insertions, 111 deletions
diff --git a/app/models/blob.rb b/app/models/blob.rb index 1cdb8811cff..a4fae22a0c4 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -28,7 +28,7 @@ class Blob < SimpleDelegator BlobViewer::Sketch, BlobViewer::Video, - + BlobViewer::PDF, BlobViewer::BinarySTL, @@ -75,19 +75,37 @@ class Blob < SimpleDelegator end def no_highlighting? - size && size > MAXIMUM_TEXT_HIGHLIGHT_SIZE + raw_size && raw_size > MAXIMUM_TEXT_HIGHLIGHT_SIZE + end + + def empty? + raw_size == 0 end def too_large? size && truncated? end + def external_storage_error? + if external_storage == :lfs + !project&.lfs_enabled? + else + false + end + end + + def stored_externally? + return @stored_externally if defined?(@stored_externally) + + @stored_externally = external_storage && !external_storage_error? + end + # Returns the size of the file that this blob represents. If this blob is an # LFS pointer, this is the size of the file stored in LFS. Otherwise, this is # the size of the blob itself. def raw_size - if valid_lfs_pointer? - lfs_size + if stored_externally? + external_size else size end @@ -98,9 +116,13 @@ class Blob < SimpleDelegator # text-based rich blob viewer matched on the file's extension. Otherwise, this # depends on the type of the blob itself. def raw_binary? - if valid_lfs_pointer? + if stored_externally? if rich_viewer rich_viewer.binary? + elsif Linguist::Language.find_by_filename(name).any? + false + elsif _mime_type + _mime_type.binary? else true end @@ -118,15 +140,7 @@ class Blob < SimpleDelegator end def readable_text? - text? && !valid_lfs_pointer? && !too_large? - end - - def valid_lfs_pointer? - lfs_pointer? && project&.lfs_enabled? - end - - def invalid_lfs_pointer? - lfs_pointer? && !project&.lfs_enabled? + text? && !stored_externally? && !too_large? end def simple_viewer @@ -165,10 +179,10 @@ class Blob < SimpleDelegator end def rich_viewer_class - return if invalid_lfs_pointer? || empty? + return if empty? || external_storage_error? classes = - if valid_lfs_pointer? + if stored_externally? BINARY_VIEWERS + TEXT_VIEWERS elsif binary? BINARY_VIEWERS diff --git a/app/models/blob_viewer/base.rb b/app/models/blob_viewer/base.rb index f944b00c9d3..a8b91d8d6bc 100644 --- a/app/models/blob_viewer/base.rb +++ b/app/models/blob_viewer/base.rb @@ -70,12 +70,13 @@ module BlobViewer return @render_error if defined?(@render_error) @render_error = - if server_side_but_stored_in_lfs? - # Files stored in LFS can only be rendered using a client-side viewer, + if server_side_but_stored_externally? + # Files that are not stored in the repository, like LFS files and + # build artifacts, can only be rendered using a client-side viewer, # since we do not want to read large amounts of data into memory on the # server side. Client-side viewers use JS and can fetch the file from # `blob_raw_url` using AJAX. - :server_side_but_stored_in_lfs + :server_side_but_stored_externally elsif override_max_size ? absolutely_too_large? : too_large? :too_large end @@ -89,8 +90,8 @@ module BlobViewer private - def server_side_but_stored_in_lfs? - server_side? && blob.valid_lfs_pointer? + def server_side_but_stored_externally? + server_side? && blob.stored_externally? end end end diff --git a/app/models/ci/artifact_blob.rb b/app/models/ci/artifact_blob.rb new file mode 100644 index 00000000000..b35febc9ac5 --- /dev/null +++ b/app/models/ci/artifact_blob.rb @@ -0,0 +1,35 @@ +module Ci + class ArtifactBlob + include BlobLike + + attr_reader :entry + + def initialize(entry) + @entry = entry + end + + delegate :name, :path, to: :entry + + def id + Digest::SHA1.hexdigest(path) + end + + def size + entry.metadata[:size] + end + + def data + "Build artifact #{path}" + end + + def mode + entry.metadata[:mode] + end + + def external_storage + :build_artifact + end + + alias_method :external_size, :size + end +end diff --git a/app/models/commit.rb b/app/models/commit.rb index bb4cb8efd15..88a015cdb77 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -236,8 +236,8 @@ class Commit project.pipelines.where(sha: sha) end - def latest_pipeline - pipelines.last + def last_pipeline + @last_pipeline ||= pipelines.last end def status(ref = nil) diff --git a/app/models/concerns/blob_like.rb b/app/models/concerns/blob_like.rb new file mode 100644 index 00000000000..adb81561000 --- /dev/null +++ b/app/models/concerns/blob_like.rb @@ -0,0 +1,48 @@ +module BlobLike + extend ActiveSupport::Concern + include Linguist::BlobHelper + + def id + raise NotImplementedError + end + + def name + raise NotImplementedError + end + + def path + raise NotImplementedError + end + + def size + 0 + end + + def data + nil + end + + def mode + nil + end + + def binary? + false + end + + def load_all_data!(repository) + # No-op + end + + def truncated? + false + end + + def external_storage + nil + end + + def external_size + nil + end +end diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index f033028c4e5..eb32bf3d32a 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -78,6 +78,9 @@ module CacheMarkdownField def cached_html_up_to_date?(markdown_field) html_field = cached_markdown_fields.html_field(markdown_field) + cached = !cached_html_for(markdown_field).nil? && !__send__(markdown_field).nil? + return false unless cached + markdown_changed = attribute_changed?(markdown_field) || false html_changed = attribute_changed?(html_field) || false diff --git a/app/models/concerns/discussion_on_diff.rb b/app/models/concerns/discussion_on_diff.rb index 8ee42875670..a7bdf5587b2 100644 --- a/app/models/concerns/discussion_on_diff.rb +++ b/app/models/concerns/discussion_on_diff.rb @@ -11,6 +11,7 @@ module DiscussionOnDiff :diff_line, :for_line?, :active?, + :created_at_diff?, to: :first_note diff --git a/app/models/concerns/note_on_diff.rb b/app/models/concerns/note_on_diff.rb index 6c27dd5aa5c..6359f7596b1 100644 --- a/app/models/concerns/note_on_diff.rb +++ b/app/models/concerns/note_on_diff.rb @@ -30,6 +30,10 @@ module NoteOnDiff raise NotImplementedError end + def created_at_diff?(diff_refs) + false + end + private def noteable_diff_refs diff --git a/app/models/diff_discussion.rb b/app/models/diff_discussion.rb index 6a6466b493b..d627fbe327f 100644 --- a/app/models/diff_discussion.rb +++ b/app/models/diff_discussion.rb @@ -10,7 +10,6 @@ class DiffDiscussion < Discussion delegate :position, :original_position, - :latest_merge_request_diff, to: :first_note @@ -18,6 +17,25 @@ class DiffDiscussion < Discussion false end + def merge_request_version_params + return unless for_merge_request? + + if active? + {} + else + diff_refs = position.diff_refs + + if diff = noteable.merge_request_diff_for(diff_refs) + { diff_id: diff.id } + elsif diff = noteable.merge_request_diff_for(diff_refs.head_sha) + { + diff_id: diff.id, + start_sha: diff_refs.start_sha + } + end + end + end + def reply_attributes super.merge( original_position: original_position.to_json, diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index abe4518d62a..76c59199afd 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -65,10 +65,11 @@ class DiffNote < Note self.position.diff_refs == diff_refs end - def latest_merge_request_diff - return unless for_merge_request? + def created_at_diff?(diff_refs) + return false unless supported? + return true if for_commit? - self.noteable.merge_request_diff_for(self.position.diff_refs) + self.original_position.diff_refs == diff_refs end private diff --git a/app/models/issue.rb b/app/models/issue.rb index 305fc01f041..78bde6820da 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -143,6 +143,14 @@ class Issue < ActiveRecord::Base branches_with_iid - branches_with_merge_request end + # Returns boolean if a related branch exists for the current issue + # ignores merge requests branchs + def has_related_branch? + project.repository.branch_names.any? do |branch| + /\A#{iid}-(?!\d+-stable)/i =~ branch + end + end + # To allow polymorphism with MergeRequest. def source_project project diff --git a/app/models/legacy_diff_discussion.rb b/app/models/legacy_diff_discussion.rb index e617ce36f56..3c1d34db5fa 100644 --- a/app/models/legacy_diff_discussion.rb +++ b/app/models/legacy_diff_discussion.rb @@ -9,14 +9,14 @@ class LegacyDiffDiscussion < Discussion memoized_values << :active - def legacy_diff_discussion? - true - end - def self.note_class LegacyDiffNote end + def legacy_diff_discussion? + true + end + def active?(*args) return @active if @active.present? @@ -27,6 +27,16 @@ class LegacyDiffDiscussion < Discussion !active? end + def merge_request_version_params + return unless for_merge_request? + + if active? + {} + else + nil + end + end + def reply_attributes super.merge(line_code: line_code) end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 9d2288c311e..12c5481cd6d 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -100,6 +100,7 @@ class MergeRequest < ActiveRecord::Base validates :merge_user, presence: true, if: :merge_when_pipeline_succeeds?, unless: :importing? validate :validate_branches, unless: [:allow_broken, :importing?, :closed_without_fork?] validate :validate_fork, unless: :closed_without_fork? + validate :validate_target_project, on: :create scope :by_source_or_target_branch, ->(branch_name) do where("source_branch = :branch OR target_branch = :branch", branch: branch_name) @@ -330,6 +331,12 @@ class MergeRequest < ActiveRecord::Base end end + def validate_target_project + return true if target_project.merge_requests_enabled? + + errors.add :base, 'Target project has disabled merge requests' + end + def validate_fork return true unless target_project && source_project return true if target_project == source_project @@ -367,12 +374,18 @@ class MergeRequest < ActiveRecord::Base merge_request_diff(true) end - def merge_request_diff_for(diff_refs) - @merge_request_diffs_by_diff_refs ||= Hash.new do |h, diff_refs| - h[diff_refs] = merge_request_diffs.viewable.select_without_diff.find_by_diff_refs(diff_refs) + def merge_request_diff_for(diff_refs_or_sha) + @merge_request_diffs_by_diff_refs_or_sha ||= Hash.new do |h, diff_refs_or_sha| + diffs = merge_request_diffs.viewable.select_without_diff + h[diff_refs_or_sha] = + if diff_refs_or_sha.is_a?(Gitlab::Diff::DiffRefs) + diffs.find_by_diff_refs(diff_refs_or_sha) + else + diffs.find_by(head_commit_sha: diff_refs_or_sha) + end end - @merge_request_diffs_by_diff_refs[diff_refs] + @merge_request_diffs_by_diff_refs_or_sha[diff_refs_or_sha] end def reload_diff_if_branch_changed diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 9bfa731785f..397dc7a25ab 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -33,7 +33,7 @@ class Namespace < ActiveRecord::Base validates :path, presence: true, length: { maximum: 255 }, - namespace: true + dynamic_path: true validate :nesting_level_allowed @@ -220,6 +220,10 @@ class Namespace < ActiveRecord::Base Project.inside_path(full_path) end + def has_parent? + parent.present? + end + private def repository_storage_paths diff --git a/app/models/note.rb b/app/models/note.rb index e720bfba030..b06985b4a6f 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -115,11 +115,19 @@ class Note < ActiveRecord::Base end def grouped_diff_discussions(diff_refs = nil) - diff_notes. - fresh. - discussions. - select { |n| n.active?(diff_refs) }. - group_by(&:line_code) + groups = {} + + diff_notes.fresh.discussions.each do |discussion| + if discussion.active?(diff_refs) + discussions = groups[discussion.line_code] ||= [] + elsif diff_refs && discussion.created_at_diff?(diff_refs) + discussions = groups[discussion.original_line_code] ||= [] + end + + discussions << discussion if discussions + end + + groups end def count_for_collection(ids, type) @@ -141,10 +149,6 @@ class Note < ActiveRecord::Base true end - def latest_merge_request_diff - nil - end - def max_attachment_size current_application_settings.max_attachment_size.megabytes.to_i end diff --git a/app/models/project.rb b/app/models/project.rb index c7dc562c238..025db89ebfd 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -196,13 +196,14 @@ class Project < ActiveRecord::Base message: Gitlab::Regex.project_name_regex_message } validates :path, presence: true, - project_path: true, + dynamic_path: true, length: { maximum: 255 }, format: { with: Gitlab::Regex.project_path_regex, - message: Gitlab::Regex.project_path_regex_message } + message: Gitlab::Regex.project_path_regex_message }, + uniqueness: { scope: :namespace_id } + validates :namespace, presence: true validates :name, uniqueness: { scope: :namespace_id } - validates :path, uniqueness: { scope: :namespace_id } validates :import_url, addressable_url: true, if: :external_import? validates :import_url, importable_url: true, if: [:external_import?, :import_url_changed?] validates :star_count, numericality: { greater_than_or_equal_to: 0 } @@ -1270,6 +1271,9 @@ class Project < ActiveRecord::Base else update_attribute(name, value) end + + rescue ActiveRecord::RecordNotSaved => e + handle_update_attribute_error(e, value) end def pushes_since_gc @@ -1314,6 +1318,14 @@ class Project < ActiveRecord::Base namespace_id_changed? end + def default_merge_request_target + if forked_from_project&.merge_requests_enabled? + forked_from_project + else + self + end + end + alias_method :name_with_namespace, :full_name alias_method :human_name, :full_name alias_method :path_with_namespace, :full_path @@ -1383,4 +1395,16 @@ class Project < ActiveRecord::Base ContainerRepository.build_root_repository(self).has_tags? end + + def handle_update_attribute_error(ex, value) + if ex.message.start_with?('Failed to replace') + if value.respond_to?(:each) + invalid = value.detect(&:invalid?) + + raise ex, ([ex.message] + invalid.errors.full_messages).join(' ') if invalid + end + end + + raise ex + end end diff --git a/app/models/project_services/chat_message/base_message.rb b/app/models/project_services/chat_message/base_message.rb index 7621a5fa2d8..e2ad586aea7 100644 --- a/app/models/project_services/chat_message/base_message.rb +++ b/app/models/project_services/chat_message/base_message.rb @@ -50,5 +50,16 @@ module ChatMessage def link(text, url) "[#{text}](#{url})" end + + def pretty_duration(seconds) + parse_string = + if duration < 1.hour + '%M:%S' + else + '%H:%M:%S' + end + + Time.at(seconds).utc.strftime(parse_string) + end end end diff --git a/app/models/project_services/chat_message/pipeline_message.rb b/app/models/project_services/chat_message/pipeline_message.rb index 4628d9b1a7b..47b68f00cff 100644 --- a/app/models/project_services/chat_message/pipeline_message.rb +++ b/app/models/project_services/chat_message/pipeline_message.rb @@ -15,7 +15,7 @@ module ChatMessage @ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch' @ref = pipeline_attributes[:ref] @status = pipeline_attributes[:status] - @duration = pipeline_attributes[:duration] + @duration = pipeline_attributes[:duration].to_i @pipeline_id = pipeline_attributes[:id] end @@ -37,7 +37,7 @@ module ChatMessage { title: "Pipeline #{pipeline_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status}", subtitle: "in #{project_link}", - text: "in #{duration} #{time_measure}", + text: "in #{pretty_duration(duration)}", image: user_avatar || '' } end @@ -45,7 +45,7 @@ module ChatMessage private def message - "#{project_link}: Pipeline #{pipeline_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{duration} #{time_measure}" + "#{project_link}: Pipeline #{pipeline_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{pretty_duration(duration)}" end def humanized_status @@ -84,9 +84,5 @@ module ChatMessage def pipeline_link "[##{pipeline_id}](#{pipeline_url})" end - - def time_measure - 'second'.pluralize(duration) - end end end diff --git a/app/models/repository.rb b/app/models/repository.rb index feabfa111fb..0c797dd5814 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -505,14 +505,8 @@ class Repository delegate :tag_names, to: :raw_repository cache_method :tag_names, fallback: [] - def branch_count - branches.size - end + delegate :branch_count, :tag_count, to: :raw_repository cache_method :branch_count, fallback: 0 - - def tag_count - raw_repository.rugged.tags.count - end cache_method :tag_count, fallback: 0 def avatar @@ -795,7 +789,7 @@ class Repository } options.merge!(get_committer_and_author(user, email: author_email, name: author_name)) - Rugged::Commit.create(rugged, options) + create_commit(options) end end # rubocop:enable Metrics/ParameterLists @@ -842,7 +836,7 @@ class Repository tree: merge_index.write_tree(rugged), ) - commit_id = Rugged::Commit.create(rugged, actual_options) + commit_id = create_commit(actual_options) merge_request.update(in_progress_merge_commit_sha: commit_id) commit_id end @@ -865,12 +859,11 @@ class Repository committer = user_to_committer(user) - Rugged::Commit.create(rugged, - message: commit.revert_message(user), - author: committer, - committer: committer, - tree: revert_tree_id, - parents: [start_commit.sha]) + create_commit(message: commit.revert_message(user), + author: committer, + committer: committer, + tree: revert_tree_id, + parents: [start_commit.sha]) end end @@ -889,16 +882,15 @@ class Repository committer = user_to_committer(user) - Rugged::Commit.create(rugged, - message: commit.message, - author: { - email: commit.author_email, - name: commit.author_name, - time: commit.authored_date - }, - committer: committer, - tree: cherry_pick_tree_id, - parents: [start_commit.sha]) + create_commit(message: commit.message, + author: { + email: commit.author_email, + name: commit.author_name, + time: commit.authored_date + }, + committer: committer, + tree: cherry_pick_tree_id, + parents: [start_commit.sha]) end end @@ -906,7 +898,7 @@ class Repository GitOperationService.new(user, self).with_branch(branch_name) do committer = user_to_committer(user) - Rugged::Commit.create(rugged, params.merge(author: committer, committer: committer)) + create_commit(params.merge(author: committer, committer: committer)) end end @@ -1148,6 +1140,12 @@ class Repository Gitlab::Metrics.add_event(event, { path: path_with_namespace }.merge(tags)) end + def create_commit(params = {}) + params[:message].delete!("\r") + + Rugged::Commit.create(rugged, params) + end + def repository_storage_path @project.repository_storage_path end diff --git a/app/models/snippet_blob.rb b/app/models/snippet_blob.rb index d6cab74eb1a..fa5fa151607 100644 --- a/app/models/snippet_blob.rb +++ b/app/models/snippet_blob.rb @@ -1,5 +1,5 @@ class SnippetBlob - include Linguist::BlobHelper + include BlobLike attr_reader :snippet @@ -28,32 +28,4 @@ class SnippetBlob Banzai.render_field(snippet, :content) end - - def mode - nil - end - - def binary? - false - end - - def load_all_data!(repository) - # No-op - end - - def lfs_pointer? - false - end - - def lfs_oid - nil - end - - def lfs_size - nil - end - - def truncated? - false - end end diff --git a/app/models/user.rb b/app/models/user.rb index bd9c9f99663..2b7ebe6c1a7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -118,7 +118,7 @@ class User < ActiveRecord::Base presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: Gitlab::Database::MAX_INT_VALUE } validates :username, - namespace: true, + dynamic_path: true, presence: true, uniqueness: { case_sensitive: false } |