summaryrefslogtreecommitdiff
path: root/app/services/projects/update_service.rb
blob: 1ea890a7add77841037b5765fead1f9ffa03a8fe (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# frozen_string_literal: true

module Projects
  class UpdateService < BaseService
    include UpdateVisibilityLevel

    ValidationError = Class.new(StandardError)

    # rubocop: disable CodeReuse/ActiveRecord
    def execute
      validate!

      ensure_wiki_exists if enabling_wiki?

      yield if block_given?

      # If the block added errors, don't try to save the project
      return update_failed! if project.errors.any?

      if project.update(params.except(:default_branch))
        after_update

        success
      else
        update_failed!
      end
    rescue ValidationError => e
      error(e.message)
    end
    # rubocop: enable CodeReuse/ActiveRecord

    def run_auto_devops_pipeline?
      return false if project.repository.gitlab_ci_yml || !project.auto_devops&.previous_changes&.include?('enabled')

      project.auto_devops_enabled?
    end

    private

    def validate!
      unless valid_visibility_level_change?(project, params[:visibility_level])
        raise ValidationError.new(_('New visibility level not allowed!'))
      end

      if renaming_project_with_container_registry_tags?
        raise ValidationError.new(_('Cannot rename project because it contains container registry tags!'))
      end

      if changing_default_branch?
        raise ValidationError.new("Could not set the default branch") unless project.change_head(params[:default_branch])
      end
    end

    def after_update
      todos_features_changes = %w(
        issues_access_level
        merge_requests_access_level
        repository_access_level
      )
      project_changed_feature_keys = project.project_feature.previous_changes.keys

      if project.previous_changes.include?(:visibility_level) && project.private?
        # don't enqueue immediately to prevent todos removal in case of a mistake
        TodosDestroyer::ProjectPrivateWorker.perform_in(Todo::WAIT_FOR_DELETE, project.id)
      elsif (project_changed_feature_keys & todos_features_changes).present?
        TodosDestroyer::PrivateFeaturesWorker.perform_in(Todo::WAIT_FOR_DELETE, project.id)
      end

      if project.previous_changes.include?('path')
        after_rename_service(project).execute
      else
        system_hook_service.execute_hooks_for(project, :update)
      end

      update_pages_config if changing_pages_related_config?
    end

    def after_rename_service(project)
      # The path slug the project was using, before the rename took place.
      path_before = project.previous_changes['path'].first

      AfterRenameService.new(project, path_before: path_before, full_path_before: project.full_path_was)
    end

    def changing_pages_related_config?
      changing_pages_https_only? || changing_pages_access_level?
    end

    def update_failed!
      model_errors = project.errors.full_messages.to_sentence
      error_message = model_errors.presence || _('Project could not be updated!')

      error(error_message)
    end

    def renaming_project_with_container_registry_tags?
      new_path = params[:path]

      new_path && new_path != project.path &&
        project.has_container_registry_tags?
    end

    def changing_default_branch?
      new_branch = params[:default_branch]

      new_branch && project.repository.exists? &&
        new_branch != project.default_branch
    end

    def enabling_wiki?
      return false if project.wiki_enabled?

      params.dig(:project_feature_attributes, :wiki_access_level).to_i > ProjectFeature::DISABLED
    end

    def changing_pages_access_level?
      params.dig(:project_feature_attributes, :pages_access_level)
    end

    def ensure_wiki_exists
      ProjectWiki.new(project, project.owner).wiki
    rescue ProjectWiki::CouldNotCreateWikiError
      log_error("Could not create wiki for #{project.full_name}")
      Gitlab::Metrics.counter(:wiki_can_not_be_created_total, 'Counts the times we failed to create a wiki')
    end

    def update_pages_config
      Projects::UpdatePagesConfigurationService.new(project).execute
    end

    def changing_pages_https_only?
      project.previous_changes.include?(:pages_https_only)
    end
  end
end