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.
This commit is contained in:
Dan Knox 2013-03-10 19:10:44 -07:00
parent 1479f17227
commit f0aa54e0fb
3 changed files with 237 additions and 0 deletions

View file

@ -0,0 +1,20 @@
namespace :gitlab do
namespace :wiki do
# This task will migrate all of the existing Wiki
# content stored in your database into the new
# Gollum Wiki system. A new repository named
# namespace/project.wiki.git will be created for
# each project that currently has Wiki pages in
# the database.
#
# Notes:
# * The existing Wiki content will remain in your
# database in-tact.
desc "GITLAB | Migrate Wiki content from database to Gollum repositories."
task :migrate => :environment do
wiki_migrator = WikiToGollumMigrator.new
wiki_migrator.migrate!
end
end
end

View file

@ -0,0 +1,103 @@
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
private
def create_gollum_repo(project)
GollumWiki.new(project, nil).wiki
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)
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)
revisions.each do |revision|
log " Creating revisions..."
# 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)
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)
attributes = page.attributes
.with_indifferent_access
.slice(:title, :content)
# Change 'index' pages to 'home' pages to match Gollum standards
if attributes[:title].downcase == "index"
attributes[:title] = "home" unless home_already_exists?(project)
end
attributes
end
def home_already_exists?(project)
project.wikis.where(title: 'home').any? || project.wikis.where(title: 'Home').any?
end
def log(message)
puts message
end
end

View file

@ -0,0 +1,114 @@
require "spec_helper"
describe WikiToGollumMigrator do
def create_wiki_for(project)
3.times { @pages[project.id] << create_page(project) }
end
def create_revisions_for(project)
@pages[project.id].each do |page|
create_revision(page)
end
end
def create_page(project)
page = project.wikis.new(title: "Page #{rand(1000)}", content: "Content")
page.user = project.owner
page.slug = page.title.parameterize
page.save!
page
end
def create_revision(page)
revision = page.dup
revision.content = "Updated Content"
revision.save!
end
def create_temp_repo(path)
FileUtils.mkdir_p path
command = "git init --quiet --bare #{path};"
system(command)
end
before do
@repo_path = "#{Rails.root}/tmp/test-git-base-path"
@projects = []
@pages = Hash.new {|h,k| h[k] = Array.new }
@projects << create(:project)
@projects << create(:project)
@projects.each do |project|
create_wiki_for project
create_revisions_for project
end
@project_without_wiki = create(:project)
end
context "Before the migration" do
it "has two projects with valid wikis" do
@projects.each do |project|
pages = project.wikis.group(:slug).all
pages.count.should == 3
end
end
it "has two revision for each page" do
@projects.each do |project|
@pages[project.id].each do |page|
revisions = project.wikis.where(slug: page.slug)
revisions.count.should == 2
end
end
end
end
describe "#initialize" do
it "finds all projects that have existing wiki pages" do
Project.count.should == 3
subject.projects.count.should == 2
end
end
context "#migrate!" do
before do
Gitlab::Shell.any_instance.stub(:add_repository) do |path|
create_temp_repo("#{@repo_path}/#{path}.git")
end
subject.stub(:log).as_null_object
subject.migrate!
end
it "creates a new Gollum Wiki for each project" do
@projects.each do |project|
wiki_path = project.path_with_namespace + ".wiki.git"
full_path = @repo_path + "/" + wiki_path
File.exist?(full_path).should be_true
File.directory?(full_path).should be_true
end
end
it "creates a gollum page for each unique Wiki page" do
@projects.each do |project|
wiki = GollumWiki.new(project, nil)
wiki.pages.count.should == 3
end
end
it "creates a new revision for each old revision of the page" do
@projects.each do |project|
wiki = GollumWiki.new(project, nil)
wiki.pages.each do |page|
page.versions.count.should == 2
end
end
end
end
end