# See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. # This migration generates missing routes for any projects and namespaces that # don't already have a route. # # On GitLab.com this would insert 611 project routes, and 0 namespace routes. # The exact number could vary per instance, so we take care of both just in # case. class GenerateMissingRoutes < ActiveRecord::Migration[4.2] include Gitlab::Database::MigrationHelpers DOWNTIME = false disable_ddl_transaction! class User < ActiveRecord::Base self.table_name = 'users' end class Route < ActiveRecord::Base self.table_name = 'routes' end module Routable def build_full_path if parent && path parent.build_full_path + '/' + path else path end end def build_full_name if parent && name parent.human_name + ' / ' + name else name end end def human_name build_full_name end def attributes_for_insert time = Time.zone.now { # We can't use "self.class.name" here as that would include the # migration namespace. source_type: source_type_for_route, source_id: id, created_at: time, updated_at: time, name: build_full_name, # The route path might already be taken. Instead of trying to generate a # new unique name on every conflict, we just append the row ID to the # route path. path: "#{build_full_path}-#{id}" } end end class Project < ActiveRecord::Base self.table_name = 'projects' include EachBatch include GenerateMissingRoutes::Routable belongs_to :namespace, class_name: 'GenerateMissingRoutes::Namespace' has_one :route, as: :source, inverse_of: :source, class_name: 'GenerateMissingRoutes::Route' alias_method :parent, :namespace alias_attribute :parent_id, :namespace_id def self.without_routes where( 'NOT EXISTS ( SELECT 1 FROM routes WHERE source_type = ? AND source_id = projects.id )', 'Project' ) end def source_type_for_route 'Project' end end class Namespace < ActiveRecord::Base self.table_name = 'namespaces' self.inheritance_column = :_type_disabled include EachBatch include GenerateMissingRoutes::Routable belongs_to :parent, class_name: 'GenerateMissingRoutes::Namespace' belongs_to :owner, class_name: 'GenerateMissingRoutes::User' has_one :route, as: :source, inverse_of: :source, class_name: 'GenerateMissingRoutes::Route' def self.without_routes where( 'NOT EXISTS ( SELECT 1 FROM routes WHERE source_type = ? AND source_id = namespaces.id )', 'Namespace' ) end def source_type_for_route 'Namespace' end end def up [Namespace, Project].each do |model| model.without_routes.each_batch(of: 100) do |batch| rows = batch.map(&:attributes_for_insert) Gitlab::Database.bulk_insert(:routes, rows) end end end def down # Removing routes we previously generated makes no sense. end end