Restream/redmine_undev_git

View on GitHub
lib/redmine_undev_git/services/migration.rb

Summary

Maintainability
B
4 hrs
Test Coverage
module RedmineUndevGit
  module Services

    class Repo < Hashie::Dash
      property :id
      property :project, required: true
      property :url
      property :identifier
      property :is_default

      def to_s
        [id, project.identifier, identifier, url].join(';')
      end
    end

    class Mapping
      attr_reader :old_repo, :new_repo, :warnings

      def initialize(old_repo, new_url, warnings)
        @old_repo, @warnings = old_repo, warnings
        @new_repo            = create_new_repo(new_url)
      end

      private

      def create_new_repo(new_url)
        Repo.new(
          project:    find_project,
          url:        new_url,
          identifier: find_identifier(new_url)
        )
      end

      def find_project
        prj = old_repo.project
        while prj.module_enabled?('issue_tracking').nil? && prj.parent
          prj = prj.parent
        end
        prj
      end

      def find_identifier(new_url)
        return old_repo.identifier if old_repo.identifier.present?
        $1 if new_url =~ /\/([\w\d\-_]+)\.git$/
      end

      def <=>(b)
        [new_repo.project.identifier, new_repo.identifier].join <=>
          [b.new_repo.project.identifier, b.new_repo.identifier].join
      end
    end

    class Migration

      def initialize(url_mapping_file)
        @url_mapping_file = url_mapping_file
      end

      def mappings
        @mappings ||= create_mappings
      end

      def url_mappings
        @url_mappings ||= read_url_mappings
      end

      def run_migration
        mappings.each_with_index do |m, i|
          puts "#{i}/#{mappings.count} Processing #{m.old_repo}"

          if m.new_repo.url.blank?
            puts "\tnew_url is not found, skipping"
            next
          end

          if same_repo = Repository.find_by_url(m.new_repo.url)
            puts "\tnew_url already used in project #{same_repo.project.identifier}"
            next
          end

          old_repo = Repository::Git.find_by_id(m.old_repo.id)
          unless old_repo
            puts "\trepository not found. Already deleted?"
            next
          end

          begin
            puts "\tfetching changesets for old repository..."
            old_repo.fetch_changesets
          rescue Exception => e
            puts "\tcan't fetch changesets: #{e.class}: #{e.message}"
          end

          begin
            new_repo = nil
            ActiveRecord::Base.transaction do
              puts "\tBegin transaction..."

              m.new_repo.project.enable_module!('hooks')

              new_repo = Repository::UndevGit.new(
                project:        m.new_repo.project,
                identifier:     m.new_repo.identifier,
                url:            m.new_repo.url,
                use_init_hooks: 0,
                use_init_refs:  1
              )
              new_repo.merge_extra_info(
                'extra_report_last_commit' => old_repo.report_last_commit
              )
              old_repo.destroy
              new_repo.save!
              puts "\tNew repository created #{m.new_repo}"
              puts "\tClonning..."
              puts "\tOk." if new_repo.scm.cloned?
            end
            puts "\tTransaction committed. Fetching changesets..."
            new_repo.fetch_changesets
            puts "\tDone. #{new_repo.changesets.count} changesets fetched."
          rescue Redmine::Scm::Adapters::CommandFailed => e
            puts "\tCommandFailed: #{e.message}"
          rescue Exception => e
            puts "\tError: unhandled exception #{e.class}: #{e.message}"
          end
        end
      end

      private

      def create_mappings
        new_urls = []
        Repository::Git.order(:id).map do |r|
          old_repo = Repo.new(
            id:         r.id,
            project:    r.project,
            url:        r.url,
            identifier: r.identifier,
            is_default: r.is_default
          )
          new_url  = url_mappings[old_repo.url]

          warnings = []
          warnings << 'new_url duplicated' if new_urls.include?(new_url)
          warnings << 'new_url is blank' if new_url.blank?

          new_urls << new_url unless new_url.blank?

          Mapping.new(old_repo, new_url, warnings)
        end
      end

      def read_url_mappings
        maps = {}

        File.open(@url_mapping_file, 'r') do |mapfile|
          mapfile.each do |line|
            old_url, new_url = *(line.split(';').map(&:strip))
            maps[old_url]    = new_url if old_url.present? && new_url.present?
          end
        end
        maps
      end

    end
  end
end