20150421120000_remove_periods_at_ends_of_usernames.rb 2.94 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
class RemovePeriodsAtEndsOfUsernames < ActiveRecord::Migration
  include Gitlab::ShellAdapter

  class Namespace < ActiveRecord::Base
    class << self
      def find_by_path_or_name(path)
        find_by("lower(path) = :path OR lower(name) = :path", path: path.downcase)
      end

      def clean_path(path)
        path = path.dup
        path.gsub!(/@.*\z/,             "")
        path.gsub!(/\.git\z/,           "")
        path.gsub!(/\A-+/,              "")
        path.gsub!(/\.+\z/,             "")
        path.gsub!(/[^a-zA-Z0-9_\-\.]/, "")

18 19 20 21
        # Users with the great usernames of "." or ".." would end up with a blank username.
        # Work around that by setting their username to "blank", followed by a counter.
        path = "blank" if path.blank?

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
        counter = 0
        base = path
        while Namespace.find_by_path_or_name(path)
          counter += 1
          path = "#{base}#{counter}"
        end

        path
      end
    end
  end

  def up
    changed_paths = {}

    select_all("SELECT id, username FROM users WHERE username LIKE '%.'").each do |user|
      username_was = user["username"]
      username = Namespace.clean_path(username_was)
      changed_paths[username_was] = username

      username = quote_string(username)
      execute "UPDATE users SET username = '#{username}' WHERE id = #{user["id"]}"
      execute "UPDATE namespaces SET path = '#{username}', name = '#{username}' WHERE type IS NULL AND owner_id = #{user["id"]}"
    end

    select_all("SELECT id, path FROM namespaces WHERE type = 'Group' AND path LIKE '%.'").each do |group|
      path_was = group["path"]
      path = Namespace.clean_path(path_was)
      changed_paths[path_was] = path

      path = quote_string(path)
      execute "UPDATE namespaces SET path = '#{path}' WHERE id = #{group["id"]}"
    end

    changed_paths.each do |path_was, path|
57 58 59
      # Don't attempt to move if original path only contains periods.
      next if path_was =~ /\A\.+\z/

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
      if gitlab_shell.mv_namespace(path_was, path)
        # If repositories moved successfully we need to remove old satellites
        # and send update instructions to users.
        # However we cannot allow rollback since we moved namespace dir
        # So we basically we mute exceptions in next actions
        begin
          gitlab_shell.rm_satellites(path_was)
          # We cannot send update instructions since models and mailers
          # can't safely be used from migrations as they may be written for 
          # later versions of the database.
          # send_update_instructions
        rescue
          # Returning false does not rollback after_* transaction but gives
          # us information about failing some of tasks
          false
        end
      else
        # 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
  end
end