Commit f0aa54e0fbce27600aa02a1ee5465e2ab5c18ccc

Authored by Dan Knox
1 parent 1479f172

Create Wiki migration task.

This commit adds a new Rake task for migrating all of your existing
Wiki content from your database into new Gollum repositories.

The bulk of the logic happens within the `WikiToGollumMigrator`
class which is decently test covered and located in the lib directory.

The new Rake task can be executed by running:

   `bundle exec rake gitlab:wiki:migrate`

It will output a nice log of every project that it migrates along
with success or failure messages.

I have used it on my own installation to migrate my Wikis successfully.
lib/tasks/gitlab/migrate_wiki.rake 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +namespace :gitlab do
  2 + namespace :wiki do
  3 +
  4 + # This task will migrate all of the existing Wiki
  5 + # content stored in your database into the new
  6 + # Gollum Wiki system. A new repository named
  7 + # namespace/project.wiki.git will be created for
  8 + # each project that currently has Wiki pages in
  9 + # the database.
  10 + #
  11 + # Notes:
  12 + # * The existing Wiki content will remain in your
  13 + # database in-tact.
  14 + desc "GITLAB | Migrate Wiki content from database to Gollum repositories."
  15 + task :migrate => :environment do
  16 + wiki_migrator = WikiToGollumMigrator.new
  17 + wiki_migrator.migrate!
  18 + end
  19 + end
  20 +end
... ...
lib/wiki_to_gollum_migrator.rb 0 → 100644
... ... @@ -0,0 +1,103 @@
  1 +class WikiToGollumMigrator
  2 +
  3 + attr_reader :projects
  4 +
  5 + def initialize
  6 + @projects = []
  7 +
  8 + Project.find_in_batches(batch_size: 50) do |batch|
  9 + batch.each { |p| @projects << p if p.wikis.any? }
  10 + end
  11 + end
  12 +
  13 + def migrate!
  14 + projects.each do |project|
  15 + log "\nMigrating Wiki for '#{project.path_with_namespace}'"
  16 + wiki = create_gollum_repo(project)
  17 + create_pages project, wiki
  18 + log "Project '#{project.path_with_namespace}' migrated. " + "[OK]".green
  19 + end
  20 + end
  21 +
  22 + private
  23 +
  24 + def create_gollum_repo(project)
  25 + GollumWiki.new(project, nil).wiki
  26 + end
  27 +
  28 + def create_pages(project, wiki)
  29 + pages = project.wikis.group(:slug).all
  30 +
  31 + pages.each do |page|
  32 + create_page_and_revisions(project, page)
  33 + end
  34 + end
  35 +
  36 + def create_page_and_revisions(project, page)
  37 + # Grab all revisions of the page
  38 + revisions = project.wikis.where(slug: page.slug).ordered.all
  39 +
  40 + # Remove the first revision created from the array
  41 + # and use it to create the Gollum page. Each successive revision
  42 + # will then be applied to the new Gollum page as an update.
  43 + first_rev = revisions.pop
  44 +
  45 + wiki = GollumWiki.new(project, page.user)
  46 + wiki_page = WikiPage.new(wiki)
  47 +
  48 + attributes = extract_attributes_from_page(first_rev)
  49 +
  50 + if wiki_page.create(attributes)
  51 + log " Created page '#{wiki_page.title}' " + "[OK]".green
  52 +
  53 + # Reverse the revisions to create them in the correct
  54 + # chronological order.
  55 + create_revisions(project, wiki_page, revisions.reverse)
  56 + else
  57 + log " Failed to create page '#{wiki_page.title}' " + "[FAILED]".red
  58 + end
  59 + end
  60 +
  61 + def create_revisions(project, page, revisions)
  62 + revisions.each do |revision|
  63 + log " Creating revisions..."
  64 + # Reinitialize a new GollumWiki instance for each page
  65 + # and revision created so the correct User is shown in
  66 + # the commit message.
  67 + wiki = GollumWiki.new(project, revision.user)
  68 + wiki_page = wiki.find_page(page.slug)
  69 +
  70 + attributes = extract_attributes_from_page(revision)
  71 +
  72 + content = attributes[:content]
  73 +
  74 + if wiki_page.update(content)
  75 + log " Created revision " + "[OK]".green
  76 + else
  77 + log " Failed to create revision " + "[FAILED]".red
  78 + end
  79 + end
  80 + end
  81 +
  82 + def extract_attributes_from_page(page)
  83 + attributes = page.attributes
  84 + .with_indifferent_access
  85 + .slice(:title, :content)
  86 +
  87 + # Change 'index' pages to 'home' pages to match Gollum standards
  88 + if attributes[:title].downcase == "index"
  89 + attributes[:title] = "home" unless home_already_exists?(project)
  90 + end
  91 +
  92 + attributes
  93 + end
  94 +
  95 + def home_already_exists?(project)
  96 + project.wikis.where(title: 'home').any? || project.wikis.where(title: 'Home').any?
  97 + end
  98 +
  99 + def log(message)
  100 + puts message
  101 + end
  102 +
  103 +end
... ...
spec/lib/wiki_to_gollum_migrator_spec.rb 0 → 100644
... ... @@ -0,0 +1,114 @@
  1 +require "spec_helper"
  2 +
  3 +describe WikiToGollumMigrator do
  4 +
  5 + def create_wiki_for(project)
  6 + 3.times { @pages[project.id] << create_page(project) }
  7 + end
  8 +
  9 + def create_revisions_for(project)
  10 + @pages[project.id].each do |page|
  11 + create_revision(page)
  12 + end
  13 + end
  14 +
  15 + def create_page(project)
  16 + page = project.wikis.new(title: "Page #{rand(1000)}", content: "Content")
  17 + page.user = project.owner
  18 + page.slug = page.title.parameterize
  19 + page.save!
  20 + page
  21 + end
  22 +
  23 + def create_revision(page)
  24 + revision = page.dup
  25 + revision.content = "Updated Content"
  26 + revision.save!
  27 + end
  28 +
  29 + def create_temp_repo(path)
  30 + FileUtils.mkdir_p path
  31 + command = "git init --quiet --bare #{path};"
  32 + system(command)
  33 + end
  34 +
  35 + before do
  36 + @repo_path = "#{Rails.root}/tmp/test-git-base-path"
  37 + @projects = []
  38 + @pages = Hash.new {|h,k| h[k] = Array.new }
  39 +
  40 + @projects << create(:project)
  41 + @projects << create(:project)
  42 +
  43 + @projects.each do |project|
  44 + create_wiki_for project
  45 + create_revisions_for project
  46 + end
  47 +
  48 + @project_without_wiki = create(:project)
  49 + end
  50 +
  51 + context "Before the migration" do
  52 + it "has two projects with valid wikis" do
  53 + @projects.each do |project|
  54 + pages = project.wikis.group(:slug).all
  55 + pages.count.should == 3
  56 + end
  57 + end
  58 +
  59 + it "has two revision for each page" do
  60 + @projects.each do |project|
  61 + @pages[project.id].each do |page|
  62 + revisions = project.wikis.where(slug: page.slug)
  63 + revisions.count.should == 2
  64 + end
  65 + end
  66 + end
  67 + end
  68 +
  69 + describe "#initialize" do
  70 + it "finds all projects that have existing wiki pages" do
  71 + Project.count.should == 3
  72 + subject.projects.count.should == 2
  73 + end
  74 + end
  75 +
  76 + context "#migrate!" do
  77 + before do
  78 + Gitlab::Shell.any_instance.stub(:add_repository) do |path|
  79 + create_temp_repo("#{@repo_path}/#{path}.git")
  80 + end
  81 +
  82 + subject.stub(:log).as_null_object
  83 +
  84 + subject.migrate!
  85 + end
  86 +
  87 + it "creates a new Gollum Wiki for each project" do
  88 + @projects.each do |project|
  89 + wiki_path = project.path_with_namespace + ".wiki.git"
  90 + full_path = @repo_path + "/" + wiki_path
  91 + File.exist?(full_path).should be_true
  92 + File.directory?(full_path).should be_true
  93 + end
  94 + end
  95 +
  96 + it "creates a gollum page for each unique Wiki page" do
  97 + @projects.each do |project|
  98 + wiki = GollumWiki.new(project, nil)
  99 + wiki.pages.count.should == 3
  100 + end
  101 + end
  102 +
  103 + it "creates a new revision for each old revision of the page" do
  104 + @projects.each do |project|
  105 + wiki = GollumWiki.new(project, nil)
  106 + wiki.pages.each do |page|
  107 + page.versions.count.should == 2
  108 + end
  109 + end
  110 + end
  111 + end
  112 +
  113 +
  114 +end
... ...