namespace.rb 9,8 КБ
Newer Older
gfyoung's avatar
gfyoung включено в состав коммита
1
2
# frozen_string_literal: true

Mark Chao's avatar
Mark Chao включено в состав коммита
3
class Namespace < ApplicationRecord
Nick Thomas's avatar
Nick Thomas включено в состав коммита
4
  include CacheMarkdownField
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
5
  include Sortable
Nick Thomas's avatar
Nick Thomas включено в состав коммита
6
  include Gitlab::VisibilityLevel
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
7
  include Routable
Douwe Maan's avatar
Douwe Maan включено в состав коммита
8
  include AfterCommitQueue
Gabriel Mazetto's avatar
Gabriel Mazetto включено в состав коммита
9
  include Storage::LegacyNamespace
Douwe Maan's avatar
Douwe Maan включено в состав коммита
10
  include Gitlab::SQL::Pattern
Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
11
  include IgnorableColumn
Zeger-Jan van de Weg's avatar
Zeger-Jan van de Weg включено в состав коммита
12
  include FeatureGate
Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
13
  include FromUnion
Mayra Cabrera's avatar
Mayra Cabrera включено в состав коммита
14
  include Gitlab::Utils::StrongMemoize
Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
15
16

  ignore_column :deleted_at
17

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
18
19
20
21
22
  # Prevent users from creating unreasonably deep level of nesting.
  # The number 20 was taken based on maximum nesting level of
  # Android repo (15) + some extra backup.
  NUMBER_OF_ANCESTORS_ALLOWED = 20

Nick Thomas's avatar
Nick Thomas включено в состав коммита
23
24
  cache_markdown_field :description, pipeline: :description

Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
25
  has_many :projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
Markus Koller's avatar
Markus Koller включено в состав коммита
26
  has_many :project_statistics
Douwe Maan's avatar
Douwe Maan включено в состав коммита
27

Kamil Trzciński's avatar
Kamil Trzciński включено в состав коммита
28
  has_many :runner_namespaces, inverse_of: :namespace, class_name: 'Ci::RunnerNamespace'
Dylan Griffith's avatar
Dylan Griffith включено в состав коммита
29
30
  has_many :runners, through: :runner_namespaces, source: :runner, class_name: 'Ci::Runner'

Douwe Maan's avatar
Douwe Maan включено в состав коммита
31
32
  # This should _not_ be `inverse_of: :namespace`, because that would also set
  # `user.namespace` when this user creates a group with themselves as `owner`.
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
33
34
  belongs_to :owner, class_name: "User"

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
35
36
  belongs_to :parent, class_name: "Namespace"
  has_many :children, class_name: "Namespace", foreign_key: :parent_id
Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
37
  has_one :chat_team, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
Mayra Cabrera's avatar
Mayra Cabrera включено в состав коммита
38
39
  has_one :root_storage_statistics, class_name: 'Namespace::RootStorageStatistics'
  has_one :aggregation_schedule, class_name: 'Namespace::AggregationSchedule'
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
40

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
41
  validates :owner, presence: true, unless: ->(n) { n.type == "Group" }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
42
  validates :name,
Robert Speicher's avatar
Robert Speicher включено в состав коммита
43
    presence: true,
Rémy Coutable's avatar
Rémy Coutable включено в состав коммита
44
45
    length: { maximum: 255 },
    namespace_name: true
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
46

Rémy Coutable's avatar
Rémy Coutable включено в состав коммита
47
  validates :description, length: { maximum: 255 }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
48
  validates :path,
Robert Speicher's avatar
Robert Speicher включено в состав коммита
49
    presence: true,
Rémy Coutable's avatar
Rémy Coutable включено в состав коммита
50
    length: { maximum: 255 },
Douwe Maan's avatar
Douwe Maan включено в состав коммита
51
    namespace_path: true
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
52

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
53
54
  validate :nesting_level_allowed

Jasper Maes's avatar
Jasper Maes включено в состав коммита
55
56
  validates_associated :runners

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
57
  delegate :name, to: :owner, allow_nil: true, prefix: true
Mayra Cabrera's avatar
Mayra Cabrera включено в состав коммита
58
  delegate :avatar_url, to: :owner, allow_nil: true
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
59

Ahmad Sherif's avatar
Ahmad Sherif включено в состав коммита
60
  after_commit :refresh_access_of_projects_invited_groups, on: :update, if: -> { previous_changes.key?('share_with_group_lock') }
Alejandro Rodríguez's avatar
Alejandro Rodríguez включено в состав коммита
61

Michael Kozono's avatar
Michael Kozono включено в состав коммита
62
63
  before_create :sync_share_with_group_lock_with_parent
  before_update :sync_share_with_group_lock_with_parent, if: :parent_changed?
Jasper Maes's avatar
Jasper Maes включено в состав коммита
64
  after_update :force_share_with_group_lock_on_descendants, if: -> { saved_change_to_share_with_group_lock? && share_with_group_lock? }
Michael Kozono's avatar
Michael Kozono включено в состав коммита
65

Gabriel Mazetto's avatar
Gabriel Mazetto включено в состав коммита
66
67
  # Legacy Storage specific hooks

Heinrich Lee Yu's avatar
Heinrich Lee Yu включено в состав коммита
68
  after_update :move_dir, if: :saved_change_to_path_or_parent?
Stan Hu's avatar
Stan Hu включено в состав коммита
69
  before_destroy(prepend: true) { prepare_for_destroy }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
70
  after_destroy :rm_dir
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
71

Douwe Maan's avatar
Douwe Maan включено в состав коммита
72
  scope :for_user, -> { where('type IS NULL') }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
73

Markus Koller's avatar
Markus Koller включено в состав коммита
74
75
76
77
78
79
80
  scope :with_statistics, -> do
    joins('LEFT JOIN project_statistics ps ON ps.namespace_id = namespaces.id')
      .group('namespaces.id')
      .select(
        'namespaces.*',
        'COALESCE(SUM(ps.storage_size), 0) AS storage_size',
        'COALESCE(SUM(ps.repository_size), 0) AS repository_size',
Peter Marko's avatar
Peter Marko включено в состав коммита
81
        'COALESCE(SUM(ps.wiki_size), 0) AS wiki_size',
Markus Koller's avatar
Markus Koller включено в состав коммита
82
        'COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size',
Alessio Caiazza's avatar
Alessio Caiazza включено в состав коммита
83
84
        'COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size',
        'COALESCE(SUM(ps.packages_size), 0) AS packages_size'
Markus Koller's avatar
Markus Koller включено в состав коммита
85
86
87
      )
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
88
89
  class << self
    def by_path(path)
Gabriel Mazetto's avatar
Gabriel Mazetto включено в состав коммита
90
      find_by('lower(path) = :value', value: path.downcase)
Douwe Maan's avatar
Douwe Maan включено в состав коммита
91
92
    end

George Tsiolis's avatar
George Tsiolis включено в состав коммита
93
    # Case insensitive search for namespace by path or name
Douwe Maan's avatar
Douwe Maan включено в состав коммита
94
95
96
97
    def find_by_path_or_name(path)
      find_by("lower(path) = :path OR lower(name) = :path", path: path.downcase)
    end

Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
98
99
100
101
102
103
104
    # Searches for namespaces matching the given query.
    #
    # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
    #
    # query - The search query as a String
    #
    # Returns an ActiveRecord::Relation
Douwe Maan's avatar
Douwe Maan включено в состав коммита
105
    def search(query)
Douwe Maan's avatar
Douwe Maan включено в состав коммита
106
      fuzzy_search(query, [:name, :path])
Douwe Maan's avatar
Douwe Maan включено в состав коммита
107
108
109
    end

    def clean_path(path)
Douwe Maan's avatar
Douwe Maan включено в состав коммита
110
      path = path.dup
Douwe Maan's avatar
Douwe Maan включено в состав коммита
111
      # Get the email username by removing everything after an `@` sign.
Will Starms's avatar
Will Starms включено в состав коммита
112
      path.gsub!(/@.*\z/,                "")
Douwe Maan's avatar
Douwe Maan включено в состав коммита
113
      # Remove everything that's not in the list of allowed characters.
Will Starms's avatar
Will Starms включено в состав коммита
114
115
116
117
118
      path.gsub!(/[^a-zA-Z0-9_\-\.]/,    "")
      # Remove trailing violations ('.atom', '.git', or '.')
      path.gsub!(/(\.atom|\.git|\.)*\z/, "")
      # Remove leading violations ('-')
      path.gsub!(/\A\-+/,                "")
Douwe Maan's avatar
Douwe Maan включено в состав коммита
119

Douwe Maan's avatar
Douwe Maan включено в состав коммита
120
      # Users with the great usernames of "." or ".." would end up with a blank username.
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
121
      # Work around that by setting their username to "blank", followed by a counter.
Douwe Maan's avatar
Douwe Maan включено в состав коммита
122
123
      path = "blank" if path.blank?

Timothy Andrew's avatar
Timothy Andrew включено в состав коммита
124
      uniquify = Uniquify.new
Timothy Andrew's avatar
Timothy Andrew включено в состав коммита
125
      uniquify.string(path) { |s| Namespace.find_by_path_or_name(s) }
Douwe Maan's avatar
Douwe Maan включено в состав коммита
126
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
127
128
  end

Nick Thomas's avatar
Nick Thomas включено в состав коммита
129
130
131
132
  def visibility_level_field
    :visibility_level
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
133
  def to_param
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
134
    full_path
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
135
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
136
137
138
139

  def human_name
    owner_name
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
140

Kamil Trzcinski's avatar
Kamil Trzcinski включено в состав коммита
141
  def any_project_has_container_registry_tags?
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
142
    all_projects.any?(&:has_container_registry_tags?)
Stan Hu's avatar
Stan Hu включено в состав коммита
143
144
145
146
  end

  def first_project_with_container_registry_tags
    all_projects.find(&:has_container_registry_tags?)
Kamil Trzcinski's avatar
Kamil Trzcinski включено в состав коммита
147
148
  end

Gabriel Mazetto's avatar
Gabriel Mazetto включено в состав коммита
149
150
  def send_update_instructions
    projects.each do |project|
Heinrich Lee Yu's avatar
Heinrich Lee Yu включено в состав коммита
151
      project.send_move_instructions("#{full_path_before_last_save}/#{project.path}")
Gabriel Mazetto's avatar
Gabriel Mazetto включено в состав коммита
152
153
154
    end
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
155
156
157
  def kind
    type == 'Group' ? 'group' : 'user'
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
158

Mayra Cabrera's avatar
Mayra Cabrera включено в состав коммита
159
160
161
162
  def user?
    kind == 'user'
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
163
  def find_fork_of(project)
Andrew Newdigate's avatar
Andrew Newdigate включено в состав коммита
164
    return unless project.fork_network
Bob Van Landuyt's avatar
Bob Van Landuyt включено в состав коммита
165

Michael Kozono's avatar
Michael Kozono включено в состав коммита
166
167
    if Gitlab::SafeRequestStore.active?
      forks_in_namespace = Gitlab::SafeRequestStore.fetch("namespaces:#{id}:forked_projects") do
Bob Van Landuyt's avatar
Bob Van Landuyt включено в состав коммита
168
169
170
171
172
173
174
175
176
        Hash.new do |found_forks, project|
          found_forks[project] = project.fork_network.find_forks_in(projects).first
        end
      end

      forks_in_namespace[project]
    else
      project.fork_network.find_forks_in(projects).first
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
177
  end
Alejandro Rodríguez's avatar
Alejandro Rodríguez включено в состав коммита
178

Patricio Cano's avatar
Patricio Cano включено в состав коммита
179
180
181
182
183
  def lfs_enabled?
    # User namespace will always default to the global setting
    Gitlab.config.lfs.enabled
  end

184
185
186
187
  def shared_runners_enabled?
    projects.with_shared_runners.any?
  end

Mario de la Ossa's avatar
Mario de la Ossa включено в состав коммита
188
189
  # Returns all ancestors, self, and descendants of the current namespace.
  def self_and_hierarchy
Jarka Košanová's avatar
Jarka Košanová включено в состав коммита
190
    Gitlab::ObjectHierarchy
Mario de la Ossa's avatar
Mario de la Ossa включено в состав коммита
191
      .new(self.class.where(id: id))
Jarka Košanová's avatar
Jarka Košanová включено в состав коммита
192
      .all_objects
Mario de la Ossa's avatar
Mario de la Ossa включено в состав коммита
193
194
  end

Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
195
  # Returns all the ancestors of the current namespaces.
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
196
  def ancestors
Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
197
    return self.class.none unless parent_id
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
198

Jarka Košanová's avatar
Jarka Košanová включено в состав коммита
199
    Gitlab::ObjectHierarchy
Grzegorz Bizon's avatar
Grzegorz Bizon включено в состав коммита
200
201
      .new(self.class.where(id: parent_id))
      .base_and_ancestors
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
202
203
  end

Takuya Noguchi's avatar
Takuya Noguchi включено в состав коммита
204
  # returns all ancestors upto but excluding the given namespace
Bob Van Landuyt's avatar
Bob Van Landuyt включено в состав коммита
205
  # when no namespace is given, all ancestors upto the top are returned
Thong Kuah's avatar
Thong Kuah включено в состав коммита
206
  def ancestors_upto(top = nil, hierarchy_order: nil)
Jarka Košanová's avatar
Jarka Košanová включено в состав коммита
207
    Gitlab::ObjectHierarchy.new(self.class.where(id: id))
Thong Kuah's avatar
Thong Kuah включено в состав коммита
208
      .ancestors(upto: top, hierarchy_order: hierarchy_order)
Bob Van Landuyt's avatar
Bob Van Landuyt включено в состав коммита
209
210
  end

Wei-Meng Lee's avatar
Wei-Meng Lee включено в состав коммита
211
  def self_and_ancestors(hierarchy_order: nil)
Nick Thomas's avatar
Nick Thomas включено в состав коммита
212
213
    return self.class.where(id: id) unless parent_id

Jarka Košanová's avatar
Jarka Košanová включено в состав коммита
214
    Gitlab::ObjectHierarchy
Nick Thomas's avatar
Nick Thomas включено в состав коммита
215
      .new(self.class.where(id: id))
Wei-Meng Lee's avatar
Wei-Meng Lee включено в состав коммита
216
      .base_and_ancestors(hierarchy_order: hierarchy_order)
Nick Thomas's avatar
Nick Thomas включено в состав коммита
217
218
  end

Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
219
  # Returns all the descendants of the current namespace.
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
220
  def descendants
Jarka Košanová's avatar
Jarka Košanová включено в состав коммита
221
    Gitlab::ObjectHierarchy
Grzegorz Bizon's avatar
Grzegorz Bizon включено в состав коммита
222
223
      .new(self.class.where(parent_id: id))
      .base_and_descendants
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
224
225
  end

Nick Thomas's avatar
Nick Thomas включено в состав коммита
226
  def self_and_descendants
Jarka Košanová's avatar
Jarka Košanová включено в состав коммита
227
    Gitlab::ObjectHierarchy
Nick Thomas's avatar
Nick Thomas включено в состав коммита
228
229
230
231
      .new(self.class.where(id: id))
      .base_and_descendants
  end

Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
232
233
234
235
  def user_ids_for_project_authorizations
    [owner_id]
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
236
237
238
239
240
241
  # Includes projects from this namespace and projects from all subgroups
  # that belongs to this namespace
  def all_projects
    Project.inside_path(full_path)
  end

Olivier Gonzalez's avatar
Olivier Gonzalez включено в состав коммита
242
243
244
245
246
247
  # Includes pipelines from this namespace and pipelines from all subgroups
  # that belongs to this namespace
  def all_pipelines
    Ci::Pipeline.where(project: all_projects)
  end

Bob Van Landuyt's avatar
Bob Van Landuyt включено в состав коммита
248
249
250
251
  def has_parent?
    parent.present?
  end

Lin Jen-Shin's avatar
Lin Jen-Shin включено в состав коммита
252
  def root_ancestor
Adam Hegyi's avatar
Adam Hegyi включено в состав коммита
253
254
255
    strong_memoize(:root_ancestor) do
      self_and_ancestors.reorder(nil).find_by(parent_id: nil)
    end
Lin Jen-Shin's avatar
Lin Jen-Shin включено в состав коммита
256
257
  end

Zeger-Jan van de Weg's avatar
Zeger-Jan van de Weg включено в состав коммита
258
259
260
261
  def subgroup?
    has_parent?
  end

Felipe Artur's avatar
Felipe Artur включено в состав коммита
262
  # Overridden on EE module
Felipe Artur's avatar
Felipe Artur включено в состав коммита
263
264
265
266
  def multiple_issue_boards_available?
    false
  end

Heinrich Lee Yu's avatar
Heinrich Lee Yu включено в состав коммита
267
268
269
  def full_path_before_last_save
    if parent_id_before_last_save.nil?
      path_before_last_save
Mayra Cabrera's avatar
Mayra Cabrera включено в состав коммита
270
    else
Heinrich Lee Yu's avatar
Heinrich Lee Yu включено в состав коммита
271
272
      previous_parent = Group.find_by(id: parent_id_before_last_save)
      previous_parent.full_path + '/' + path_before_last_save
Mayra Cabrera's avatar
Mayra Cabrera включено в состав коммита
273
    end
Nick Thomas's avatar
Nick Thomas включено в состав коммита
274
275
  end

Francisco Javier López's avatar
Francisco Javier López включено в состав коммита
276
277
278
279
  def refresh_project_authorizations
    owner.refresh_authorized_projects
  end

Mayra Cabrera's avatar
Mayra Cabrera включено в состав коммита
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
  def auto_devops_enabled?
    first_auto_devops_config[:status]
  end

  def first_auto_devops_config
    return { scope: :group, status: auto_devops_enabled } unless auto_devops_enabled.nil?

    strong_memoize(:first_auto_devops_config) do
      if has_parent?
        parent.first_auto_devops_config
      else
        { scope: :instance, status: Gitlab::CurrentSettings.auto_devops_enabled? }
      end
    end
  end

Alejandro Rodríguez's avatar
Alejandro Rodríguez включено в состав коммита
296
297
  private

Heinrich Lee Yu's avatar
Heinrich Lee Yu включено в состав коммита
298
299
300
301
302
303
304
305
306
  def parent_changed?
    parent_id_changed?
  end

  def saved_change_to_parent?
    saved_change_to_parent_id?
  end

  def saved_change_to_path_or_parent?
Jasper Maes's avatar
Jasper Maes включено в состав коммита
307
    saved_change_to_path? || saved_change_to_parent_id?
Mayra Cabrera's avatar
Mayra Cabrera включено в состав коммита
308
309
  end

Ahmad Sherif's avatar
Ahmad Sherif включено в состав коммита
310
  def refresh_access_of_projects_invited_groups
Grzegorz Bizon's avatar
Grzegorz Bizon включено в состав коммита
311
312
313
314
    Group
      .joins(project_group_links: :project)
      .where(projects: { namespace_id: id })
      .find_each(&:refresh_members_authorized_projects)
Ahmad Sherif's avatar
Ahmad Sherif включено в состав коммита
315
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
316

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
317
318
319
320
321
  def nesting_level_allowed
    if ancestors.count > Group::NUMBER_OF_ANCESTORS_ALLOWED
      errors.add(:parent_id, "has too deep level of nesting")
    end
  end
Michael Kozono's avatar
Michael Kozono включено в состав коммита
322
323

  def sync_share_with_group_lock_with_parent
Michael Kozono's avatar
Michael Kozono включено в состав коммита
324
    if parent&.share_with_group_lock?
Michael Kozono's avatar
Michael Kozono включено в состав коммита
325
326
327
328
329
      self.share_with_group_lock = true
    end
  end

  def force_share_with_group_lock_on_descendants
Jarka Košanová's avatar
Jarka Košanová включено в состав коммита
330
    return unless Group.supports_nested_objects?
Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
331
332
333
334
335
336
337

    # We can't use `descendants.update_all` since Rails will throw away the WITH
    # RECURSIVE statement. We also can't use WHERE EXISTS since we can't use
    # different table aliases, hence we're just using WHERE IN. Since we have a
    # maximum of 20 nested groups this should be fine.
    Namespace.where(id: descendants.select(:id))
      .update_all(share_with_group_lock: true)
Michael Kozono's avatar
Michael Kozono включено в состав коммита
338
  end
Mayra Cabrera's avatar
Mayra Cabrera включено в состав коммита
339

Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre включено в состав коммита
340
341
  def write_projects_repository_config
    all_projects.find_each do |project|
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre включено в состав коммита
342
      project.write_repository_config
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre включено в состав коммита
343
      project.track_project_repository
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre включено в состав коммита
344
345
    end
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets включено в состав коммита
346
end