wiki_to_gollum_migrator.rb 3.39 KB
class WikiToGollumMigrator

  attr_reader :projects

  def initialize
    @projects = []

    Project.find_in_batches(batch_size: 50) do |batch|
      batch.each { |p| @projects << p if p.wikis.any? }
    end
  end

  def migrate!
    projects.each do |project|
      log "\nMigrating Wiki for '#{project.path_with_namespace}'"
      wiki = create_gollum_repo(project)
      create_pages project, wiki
      log "Project '#{project.path_with_namespace}' migrated. " + "[OK]".green
    end
  end

  def rollback!
    log "\nBeginning Wiki Migration Rollback..."
    projects.each do |project|
      destroy_gollum_repo project
    end
    log "\nWiki Rollback Complete."
  end

  private

  def create_gollum_repo(project)
    GollumWiki.new(project, nil).wiki
  end

  def destroy_gollum_repo(project)
    log "  Removing Wiki repo for project: #{project.path_with_namespace}"
    path = GollumWiki.new(project, nil).path_with_namespace
    if Gitlab::Shell.new.remove_repository(path)
      log "  Wiki destroyed successfully. " + "[OK}".green
    else
      log "  Problem destroying wiki. Please remove it manually. " + "[FAILED]".red
    end
  end

  def create_pages(project, wiki)
    pages = project.wikis.group(:slug).all

    pages.each do |page|
      create_page_and_revisions(project, page)
    end
  end

  def create_page_and_revisions(project, page)
    # Grab all revisions of the page
    revisions = project.wikis.where(slug: page.slug).ordered.all

    # Remove the first revision created from the array
    # and use it to create the Gollum page. Each successive revision
    # will then be applied to the new Gollum page as an update.
    first_rev = revisions.pop

    wiki = GollumWiki.new(project, page.user)
    wiki_page = WikiPage.new(wiki)

    attributes = extract_attributes_from_page(first_rev, project)

    log "  Creating page '#{first_rev.title}'..."
    if wiki_page.create(attributes)
      log "  Created page '#{wiki_page.title}' " + "[OK]".green

      # Reverse the revisions to create them in the correct
      # chronological order.
      create_revisions(project, wiki_page, revisions.reverse)
    else
      log "  Failed to create page '#{wiki_page.title}' " + "[FAILED]".red
    end
  end

  def create_revisions(project, page, revisions)
    log "    Creating revisions..."
    revisions.each do |revision|
      # Reinitialize a new GollumWiki instance for each page
      # and revision created so the correct User is shown in
      # the commit message.
      wiki = GollumWiki.new(project, revision.user)
      wiki_page = wiki.find_page(page.slug)

      attributes = extract_attributes_from_page(revision, project)

      content = attributes[:content]

      if wiki_page.update(content)
        log "    Created revision " + "[OK]".green
      else
        log "    Failed to create revision " + "[FAILED]".red
      end
    end
  end

  def extract_attributes_from_page(page, project)
    attributes = page.attributes
                     .with_indifferent_access
                     .slice(:title, :content)

    slug = page.slug

    # Change 'index' pages to 'home' pages to match Gollum standards
    if slug.downcase == "index"
      attributes[:title] = "home" unless home_already_exists?(project)
    end

    attributes
  end

  def home_already_exists?(project)
    project.wikis.where(slug: 'home').any? || project.wikis.where(slug: 'Home').any?
  end

  def log(message)
    puts message
  end

end