-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathconverter.rb
165 lines (147 loc) · 4.92 KB
/
converter.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
require 'nokogiri'
require 'fileutils'
require 'time'
require 'log4r'
require 'pp'
GITIGNORE = <<-EOF
[Oo]bj/
[Bb]in/
*.suo
*.user
*.vspscc
*.vssscc
*.tmp
*.log
EOF
class Converter
# Configure logging
include Log4r
$logger = Logger.new('vault2git')
stdout_log = StdoutOutputter.new('console')
stdout_log.level = INFO
file_log = FileOutputter.new('file', :filename => $options.logfile, :trunc => true)
file_log.level = DEBUG
$logger.add(stdout_log, file_log)
%w(debug info warn error fatal).map(&:to_sym).each do |level|
(class << self; self; end).instance_eval do
define_method level do |msg|
$logger.send level, msg
end
end
end
debug $options.inspect
def self.quote_param(param)
value = $options.send(param)
quote_value value
end
def self.quote_value(value)
return '' unless value
value.include?(' ') ? '"' + value + '"' : value
end
def self.vault_command(command, options = [], args = [], append_source_folder = true)
parts = []
parts << quote_param(:vault_client)
parts << command
%w(host username password repository).each{|param| parts << "-#{param} #{quote_param(param)}"}
[*options].each{|param| parts << param}
parts << quote_param(:source) if append_source_folder
[*args].each{|param| parts << quote_value(param)}
cmd = parts.join(' ')
debug "Invoking vault: #{cmd}"
retryable do
begin
xml = `#{cmd}`
doc = Nokogiri::XML(xml) do |config|
config.strict.noblanks
end
raise "Unsuccessful command '#{command}': #{(doc % :error).text}" if (doc % :result)[:success] == 'no'
doc
rescue Exception => e
raise #"Error processing command '#{cmd}'", e
end
end
end
def self.git_command(command, *options)
parts = []
parts << quote_param(:git)
parts << command
[*options].each{|param| parts << param}
cmd = parts.join(' ')
debug "Invoking git: #{cmd}"
begin
debug output = retryable{`#{cmd}`}
rescue Exception => e
raise "Error processing command '#{command}'", e
end
end
def self.git_commit(comments, *options)
git_command 'add', '--all', '.'
params = [*comments].map{|c| "-m \"#{c}\""} << options << "-a"
git_command 'commit', *(params.flatten)
end
def self.retryable(max_times = 5, &block)
tries = 0
begin
yield block
rescue
tries += 1
if tries <= max_times
warn "Retrying command, take #{tries} of #{max_times}"
retry
end
error "Giving up retrying"
raise
end
end
def self.clear_working_folder
files_to_delete = Dir[$options.dest + "/*"]
debug "Removing folders: #{files_to_delete.join(', ')}"
files_to_delete .each{|d| FileUtils.rm_rf d}
end
def self.convert
info "Starting at #{Time.now}"
debug "Parameters: " + $options.inspect
authors = get_authors()
info "Prepare destination folder"
FileUtils.rm_rf $options.dest
git_command 'init', quote_value($options.dest)
Dir.chdir $options.dest
File.open(".gitignore", 'w') {|f| f.write(GITIGNORE)}
git_commit 'Starting Vault repository import'
info "Set Vault working folder"
vault_command 'setworkingfolder', quote_value($options.source), $options.dest, false
info "Fetch version history"
versions = vault_command('versionhistory', ["-rowlimit 0"]) % :history
versions = versions.children.map do |item|
hash = {}
item.attributes.each do |attr|
hash[attr[0].to_sym] = attr[1].value
end
hash
end
count = 0
versions.sort_by {|v| v[:version].to_i}.each_with_index do |version, i|
count += 1
info "Processing version #{count} of #{versions.size}"
clear_working_folder
vault_command 'getversion', ["-backup no", "-merge overwrite", "-setfiletime checkin", "-performdeletions removeworkingcopy", version[:version]], $options.dest
comments = [version[:comment], "Original Vault commit: version #{version[:version]} on #{version[:date]} by #{version[:user]} (txid=#{version[:txid]})"].compact.map{|c|c.gsub('"', '\"')}
git_commit comments, "--date=\"#{Time.parse(version[:date]).strftime('%Y-%m-%dT%H:%M:%S')}\"", (if authors.has_key? version[:user] then "--author=\"#{authors[version[:user]]}\"" else "" end)
git_command 'gc' if count % 20 == 0 || count == versions.size
GC.start if count % 20 == 0 # Force Ruby GC (might speed things up?)
end
info "Ended at #{Time.now}"
end
AUTHORS_FILE = "authors.xml"
def self.get_authors
authors = Hash.new()
if File.exists? AUTHORS_FILE then
info "Reading authors file"
doc = Nokogiri::XML(File.open(AUTHORS_FILE))
doc.children.each do |item|
authors[item[:vaultname]] = "#{item[:name]} <#{item[:email]}>"
end
end
authors
end
end