Welcome to mirror list, hosted at ThFree Co, Russian Federation.

diff_check.rb « checks « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: a359236e1502686f525a422cdbaf2dde36d0edf9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# frozen_string_literal: true

module Gitlab
  module Checks
    class DiffCheck < BaseSingleChecker
      include Gitlab::Utils::StrongMemoize

      LOG_MESSAGES = {
        validate_file_paths: "Validating diffs' file paths..."
      }.freeze

      def validate!
        # git-notes stores notes history as commits in refs/notes/commits (by
        # default but is configurable) so we restrict the diff checks to tag
        # and branch refs
        return unless tag_ref? || branch_ref?
        return if deletion?
        return unless should_run_validations?
        return if commits.empty?

        paths = project.repository.find_changed_paths(treeish_objects, merge_commit_diff_mode: :all_parents)
        paths.each do |path|
          validate_path(path)
        end

        validate_file_paths(paths.map(&:path).uniq)
      end

      private

      def treeish_objects
        objects = commits

        return objects unless project.repository.empty?

        # It's a special case for the push to the empty repository
        #
        # Git doesn't display a diff of the initial commit of the repository
        # if we just provide a commit sha.
        #
        # To fix that we can use TreeRequest to check the difference
        # between empty tree sha and the tree sha of the initial commit
        #
        # `commits` are sorted in reverse order, the initial commit is the last one.
        init_commit = objects.last

        diff_tree = Gitlab::Git::DiffTree.from_commit(init_commit)
        return [diff_tree] + objects if diff_tree

        objects
      end

      def validate_lfs_file_locks?
        strong_memoize(:validate_lfs_file_locks) do
          project.lfs_enabled? && project.any_lfs_file_locks?
        end
      end

      def should_run_validations?
        validations_for_path.present? || file_paths_validations.present?
      end

      def validate_path(path)
        validations_for_path.each do |validation|
          if error = validation.call(path)
            raise ::Gitlab::GitAccess::ForbiddenError, error
          end
        end
      end

      # Method overwritten in EE to inject custom validations
      def validations_for_path
        []
      end

      def file_paths_validations
        validate_lfs_file_locks? ? [lfs_file_locks_validation] : []
      end

      def validate_file_paths(file_paths)
        logger.log_timed(LOG_MESSAGES[__method__]) do
          file_paths_validations.each do |validation|
            if error = validation.call(file_paths)
              raise ::Gitlab::GitAccess::ForbiddenError, error
            end
          end
        end
      end

      # rubocop: disable CodeReuse/ActiveRecord
      def lfs_file_locks_validation
        lambda do |paths|
          lfs_lock = project.lfs_file_locks.where(path: paths).where.not(user_id: user_access.user.id).take

          if lfs_lock
            return "The path '#{lfs_lock.path}' is locked in Git LFS by #{lfs_lock.user.username}"
          end
        end
      end
      # rubocop: enable CodeReuse/ActiveRecord
    end
  end
end

Gitlab::Checks::DiffCheck.prepend_mod_with('Gitlab::Checks::DiffCheck')