137 lines
3.3 KiB
Ruby
137 lines
3.3 KiB
Ruby
class RenameReservedProjectNames < ActiveRecord::Migration[4.2]
|
|
include Gitlab::Database::MigrationHelpers
|
|
|
|
DOWNTIME = false
|
|
|
|
THREAD_COUNT = 8
|
|
|
|
KNOWN_PATHS = %w(.well-known
|
|
all
|
|
blame
|
|
blob
|
|
commits
|
|
create
|
|
create_dir
|
|
edit
|
|
files
|
|
find_file
|
|
groups
|
|
hooks
|
|
issues
|
|
logs_tree
|
|
merge_requests
|
|
new
|
|
preview
|
|
projects
|
|
raw
|
|
repository
|
|
robots.txt
|
|
s
|
|
snippets
|
|
teams
|
|
tree
|
|
u
|
|
unsubscribes
|
|
update
|
|
users
|
|
wikis).freeze
|
|
|
|
def up
|
|
queues = Array.new(THREAD_COUNT) { Queue.new }
|
|
start = false
|
|
|
|
threads = Array.new(THREAD_COUNT) do |index|
|
|
Thread.new do
|
|
queue = queues[index]
|
|
|
|
# Wait until we have input to process.
|
|
until start; end
|
|
|
|
rename_projects(queue.pop) until queue.empty?
|
|
end
|
|
end
|
|
|
|
enum = queues.each
|
|
|
|
reserved_projects.each_slice(100) do |slice|
|
|
begin
|
|
queue = enum.next
|
|
rescue StopIteration
|
|
enum.rewind
|
|
retry
|
|
end
|
|
|
|
queue << slice
|
|
end
|
|
|
|
start = true
|
|
|
|
threads.each(&:join)
|
|
end
|
|
|
|
def down
|
|
# nothing to do here
|
|
end
|
|
|
|
private
|
|
|
|
def reserved_projects
|
|
Project.unscoped
|
|
.includes(:namespace)
|
|
.where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)')
|
|
.where('projects.path' => KNOWN_PATHS)
|
|
end
|
|
|
|
def route_exists?(full_path)
|
|
quoted_path = ActiveRecord::Base.connection.quote_string(full_path)
|
|
|
|
ActiveRecord::Base.connection
|
|
.select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
|
|
end
|
|
|
|
# Adds number to the end of the path that is not taken by other route
|
|
def rename_path(namespace_path, path_was)
|
|
counter = 0
|
|
path = "#{path_was}#{counter}"
|
|
|
|
while route_exists?("#{namespace_path}/#{path}")
|
|
counter += 1
|
|
path = "#{path_was}#{counter}"
|
|
end
|
|
|
|
path
|
|
end
|
|
|
|
def rename_projects(projects)
|
|
projects.each do |project|
|
|
id = project.id
|
|
path_was = project.path
|
|
namespace_path = project.namespace.path
|
|
path = rename_path(namespace_path, path_was)
|
|
|
|
begin
|
|
# Because project path update is quite complex operation we can't safely
|
|
# copy-paste all code from GitLab. As exception we use Rails code here
|
|
if rename_project_row(project, path)
|
|
after_rename_service(project, path_was, namespace_path).execute
|
|
end
|
|
rescue Exception => e # rubocop: disable Lint/RescueException
|
|
Rails.logger.error "Exception when renaming project #{id}: #{e.message}"
|
|
end
|
|
end
|
|
end
|
|
|
|
def rename_project_row(project, path)
|
|
project.respond_to?(:update_attributes) &&
|
|
project.update(path: path) &&
|
|
defined?(Projects::AfterRenameService)
|
|
end
|
|
|
|
def after_rename_service(project, path_was, namespace_path)
|
|
AfterRenameService.new(
|
|
project,
|
|
path_before: path_was,
|
|
full_path_before: "#{namespace_path}/#{path_was}"
|
|
).execute
|
|
end
|
|
end
|