diff --git a/changelogs/unreleased/dz-rename-invalid-users.yml b/changelogs/unreleased/dz-rename-invalid-users.yml new file mode 100644 index 0000000000000000000000000000000000000000..f420b069531231d4f9d98b5a18f0d3635e2aac78 --- /dev/null +++ b/changelogs/unreleased/dz-rename-invalid-users.yml @@ -0,0 +1,4 @@ +--- +title: Rename users with namespace ending with .git +merge_request: 8309 +author: diff --git a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb new file mode 100644 index 0000000000000000000000000000000000000000..809b09feb849223274cdb6db76cfc02723e02e07 --- /dev/null +++ b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb @@ -0,0 +1,87 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveDotGitFromUsernames < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + include Gitlab::ShellAdapter + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + def up + invalid_users.each do |user| + id = user['id'] + namespace_id = user['namespace_id'] + path_was = user['username'] + path_was_wildcard = quote_string("#{path_was}/%") + path = quote_string(rename_path(path_was)) + + move_namespace(namespace_id, path_was, path) + + execute "UPDATE routes SET path = '#{path}' WHERE source_type = 'Namespace' AND source_id = #{namespace_id}" + execute "UPDATE namespaces SET path = '#{path}' WHERE id = #{namespace_id}" + execute "UPDATE users SET username = '#{path}' WHERE id = #{id}" + + select_all("SELECT id, path FROM routes WHERE path LIKE '#{path_was_wildcard}'").each do |route| + new_path = "#{path}/#{route['path'].split('/').last}" + execute "UPDATE routes SET path = '#{new_path}' WHERE id = #{route['id']}" + end + end + end + + def down + # nothing to do here + end + + private + + def invalid_users + select_all("SELECT u.id, u.username, n.path AS namespace_path, n.id AS namespace_id FROM users u + INNER JOIN namespaces n ON n.owner_id = u.id + WHERE n.type is NULL AND n.path LIKE '%.git'") + end + + def route_exists?(path) + select_all("SELECT id, path FROM routes WHERE path = '#{quote_string(path)}'").present? + end + + # Accepts invalid path like test.git and returns test_git or + # test_git1 if test_git already taken + def rename_path(path) + # To stay closer with original name and reduce risk of duplicates + # we rename suffix instead of removing it + path = path.sub(/\.git\z/, '_git') + + counter = 0 + base = path + + while route_exists?(path) + counter += 1 + path = "#{base}#{counter}" + end + + path + end + + def move_namespace(namespace_id, path_was, path) + repository_storage_paths = select_all("SELECT distinct(repository_storage) FROM projects WHERE namespace_id = #{namespace_id}").map do |row| + Gitlab.config.repositories.storages[row['repository_storage']] + end.compact + + # Move the namespace directory in all storages paths used by member projects + repository_storage_paths.each do |repository_storage_path| + # Ensure old directory exists before moving it + gitlab_shell.add_namespace(repository_storage_path, path_was) + + unless gitlab_shell.mv_namespace(repository_storage_path, path_was, path) + Rails.logger.error "Exception moving path #{repository_storage_path} from #{path_was} to #{path}" + + # if we cannot move namespace directory we should rollback + # db changes in order to prevent out of sync between db and fs + raise Exception.new('namespace directory cannot be moved') + end + end + + Gitlab::UploadsTransfer.new.rename_namespace(path_was, path) + end +end diff --git a/db/schema.rb b/db/schema.rb index 05b6c807660180e493eeb08ff8490f99db2d91b1..1fcb6bcfe60b9b6bfb8250e0015fbacd0eaea1e0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161221140236) do +ActiveRecord::Schema.define(version: 20161226122833) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/spec/migrations/remove_dot_git_from_usernames.rb b/spec/migrations/remove_dot_git_from_usernames.rb new file mode 100644 index 0000000000000000000000000000000000000000..1b1d2adc46386cf0bd7c44162c90220faf1db5f0 --- /dev/null +++ b/spec/migrations/remove_dot_git_from_usernames.rb @@ -0,0 +1,29 @@ +# encoding: utf-8 + +require 'spec_helper' +require Rails.root.join('db', 'migrate', '20161226122833_remove_dot_git_from_usernames.rb') + +describe RemoveDotGitFromUsernames do + let(:user) { create(:user) } + + describe '#up' do + let(:migration) { described_class.new } + + before do + namespace = user.namespace + namespace.path = 'test.git' + namespace.save!(validate: false) + + user.username = 'test.git' + user.save!(validate: false) + end + + it 'renames user with .git in username' do + migration.up + + expect(user.reload.username).to eq('test_git') + expect(user.namespace.reload.path).to eq('test_git') + expect(user.namespace.route.path).to eq('test_git') + end + end +end