whistle 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/History.txt +3 -0
  2. data/README.txt +38 -0
  3. data/bin/whistle +90 -0
  4. data/lib/config.rb +19 -0
  5. data/lib/phash.rb +16 -0
  6. data/lib/relay.rb +24 -0
  7. data/lib/resource.rb +113 -0
  8. data/lib/ssl_patch.rb +15 -0
  9. data/lib/switchbox.rb +54 -0
  10. data/lib/time_ext.rb +30 -0
  11. data/lib/version.rb +3 -0
  12. data/sample/config.yml +12 -0
  13. data/vendor/rscm-0.5.1-patched-stripped/README +218 -0
  14. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm.rb +14 -0
  15. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/abstract_log_parser.rb +35 -0
  16. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/base.rb +289 -0
  17. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/command_line.rb +146 -0
  18. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/difftool.rb +44 -0
  19. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/line_editor.rb +46 -0
  20. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/mockit.rb +157 -0
  21. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/parser.rb +39 -0
  22. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/path_converter.rb +60 -0
  23. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/platform.rb +26 -0
  24. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/revision.rb +103 -0
  25. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/revision_file.rb +85 -0
  26. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/revision_poller.rb +93 -0
  27. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/revisions.rb +79 -0
  28. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/clearcase.rb +182 -0
  29. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/cvs.rb +374 -0
  30. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/cvs_log_parser.rb +154 -0
  31. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/darcs.rb +120 -0
  32. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/darcs_log_parser.rb +65 -0
  33. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/monotone.rb +338 -0
  34. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/monotone_log_parser.rb +109 -0
  35. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/mooky.rb +6 -0
  36. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/perforce.rb +216 -0
  37. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/star_team.rb +104 -0
  38. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/subversion.rb +397 -0
  39. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/subversion_log_parser.rb +165 -0
  40. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/tempdir.rb +17 -0
  41. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/time_ext.rb +11 -0
  42. data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/version.rb +13 -0
  43. data/vendor/ruby-feedparser-0.5-stripped/README +14 -0
  44. data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser.rb +28 -0
  45. data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/feedparser.rb +300 -0
  46. data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/filesizes.rb +12 -0
  47. data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/html-output.rb +126 -0
  48. data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/html2text-parser.rb +409 -0
  49. data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/rexml_patch.rb +28 -0
  50. data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/sgml-parser.rb +332 -0
  51. data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/text-output.rb +83 -0
  52. data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/textconverters.rb +120 -0
  53. metadata +132 -0
@@ -0,0 +1,154 @@
1
+ require 'rscm/revision'
2
+ require 'rscm/abstract_log_parser'
3
+ require 'ftools'
4
+ require 'time'
5
+
6
+ module RSCM
7
+
8
+ class CvsLogParser < AbstractLogParser
9
+ REVISION_SEPARATOR = /^----------------------------$/ unless defined? REVISION_SEPARATOR
10
+ ENTRY_SEPARATOR = /^=============================================================================$/ unless defined? ENTRY_SEPARATOR
11
+
12
+ attr_accessor :cvspath
13
+ attr_accessor :cvsmodule
14
+
15
+ def initialize(io)
16
+ super(io)
17
+ end
18
+
19
+ def parse_revisions
20
+ revisions = Revisions.new
21
+ while(log_entry = next_log_entry)
22
+ begin
23
+ parse_files(log_entry, revisions)
24
+ rescue Exception => e
25
+ $stderr.puts("could not parse log entry: #{log_entry}\ndue to: #{e.message}\n\t")
26
+ $stderr.puts(e.backtrace.join("\n\t"))
27
+ end
28
+ end
29
+ revisions.sort!
30
+ revisions
31
+ end
32
+
33
+ def next_log_entry
34
+ read_until_matching_line(ENTRY_SEPARATOR)
35
+ end
36
+
37
+ def split_entries(log_entry)
38
+ entries = [""]
39
+ log_entry.each_line do |line|
40
+ if line=~REVISION_SEPARATOR
41
+ entries << ""
42
+ else
43
+ entries[entries.length-1] << line
44
+ end
45
+ end
46
+ entries
47
+ end
48
+
49
+ def parse_files(log_entry, revisions)
50
+ entries = split_entries(log_entry)
51
+
52
+ entries[1..entries.length].each do |entry|
53
+ file = parse_file(entry)
54
+ next if file.nil?
55
+ file.path = parse_path(entries[0])
56
+ file.status = RevisionFile::ADDED if file.native_revision_identifier =~ /1\.1$/
57
+ revisions.add(file)
58
+ end
59
+ nil
60
+ end
61
+
62
+ def parse_head_revision(first_entry)
63
+ head_revision = extract_match(first_entry, /^head: (.*?)$/m)
64
+ end
65
+
66
+ def parse_path(first_entry)
67
+ working_file = extract_match(first_entry, /^Working file: (.*?)$/m)
68
+ return convert_all_slashes_to_forward_slashes(working_file) unless working_file.nil? || working_file == ""
69
+ make_relative_to_module(extract_required_match(first_entry, /^RCS file: (.*?)(,v|$)/m))
70
+ end
71
+
72
+ def make_relative_to_module(file)
73
+ return file if cvspath.nil? || cvsmodule.nil? || file.nil?
74
+ cvspath = convert_all_slashes_to_forward_slashes(self.cvspath)
75
+ convert_all_slashes_to_forward_slashes(file).gsub(/^#{cvspath}\/#{cvsmodule}\//, "")
76
+ end
77
+
78
+ def parse_file(file_entry)
79
+ raise "can't parse: #{file_entry}" if file_entry =~ REVISION_SEPARATOR
80
+
81
+ file_entry_lines = file_entry.split(/\r?\n/)
82
+
83
+ file = RevisionFile.new
84
+ file.native_revision_identifier = extract_match(file_entry_lines[0], /revision (.*)$/)
85
+ file.previous_native_revision_identifier = determine_previous_native_revision_identifier(file.native_revision_identifier)
86
+
87
+ time = extract_required_match(file_entry_lines[1], /date: (.*?)(;|$)/)
88
+ if(time.strip.length == 19)
89
+ # CVS 1.11.x doesn't specify timezone (but assumes UTC), so we'll add it here.
90
+ time += " +0000"
91
+ end
92
+ file.time = Time.parse(time).utc
93
+ file.developer = extract_match(file_entry_lines[1], /author: (.*?);/)
94
+
95
+ state = extract_match(file_entry_lines[1], /state: (.*?);/)
96
+ file.status = STATES[state]
97
+
98
+ message_start = 2
99
+ branches = nil
100
+ if(file_entry_lines[2] =~ /^branches:\s+(.*);/)
101
+ message_start = 3
102
+ branches = $1
103
+ end
104
+
105
+ file.message = file_entry_lines[message_start..-1].join("\n")
106
+
107
+ # Ignore the initial revision from import - we will have two of them
108
+ if(file.message == "Initial revision" && branches == "1.1.1")
109
+ return nil
110
+ end
111
+
112
+ file
113
+ end
114
+
115
+ def determine_previous_native_revision_identifier(revision)
116
+ if revision =~ /(.*)\.(.*)/
117
+ big_version_number = $1
118
+ small_version_number = $2.to_i
119
+ if small_version_number == 1
120
+ nil
121
+ else
122
+ "#{big_version_number}.#{small_version_number - 1}"
123
+ end
124
+ else
125
+ nil
126
+ end
127
+ end
128
+
129
+ def extract_required_match(string, regexp)
130
+ if(string =~ regexp)
131
+ return($1)
132
+ else
133
+ $stderr.puts("can't parse: '#{string}'\nexpected to match regexp: #{regexp.to_s}")
134
+ end
135
+ end
136
+
137
+ def extract_match(string, regexp)
138
+ if string=~regexp
139
+ return($1)
140
+ else
141
+ ""
142
+ end
143
+ end
144
+
145
+ private
146
+
147
+ # The state field is "Exp" both for added and modified files. retards!
148
+ # We need some additional logic to figure out whether it is added or not.
149
+ # Maybe look at the revision. (1.1 means new I think. - deal with it later)
150
+ STATES = {"dead" => RevisionFile::DELETED, "Exp" => RevisionFile::MODIFIED} unless defined? STATES
151
+
152
+ end
153
+
154
+ end
@@ -0,0 +1,120 @@
1
+ require 'tempfile'
2
+ require 'fileutils'
3
+ require 'rscm'
4
+
5
+ module RSCM
6
+ class Darcs < Base
7
+ # See http://progetti.arstecnica.it/tailor/browser/vcpx/darcs.py
8
+ attr_accessor :dir
9
+
10
+ def installed?
11
+ begin
12
+ darcs("--version", {})
13
+ true
14
+ rescue
15
+ false
16
+ end
17
+ end
18
+
19
+ def initialize(dir=".")
20
+ @dir = File.expand_path(dir)
21
+ end
22
+
23
+ def can_create_central?
24
+ true
25
+ end
26
+
27
+ def create_central
28
+ with_working_dir(@dir) do
29
+ darcs("initialize")
30
+ end
31
+ end
32
+
33
+ def import_central(dir, message)
34
+ ENV["EMAIL"] = "dcontrol@codehaus.org"
35
+ FileUtils.cp_r(Dir.glob("#{dir}/*"), @dir)
36
+ with_working_dir(@dir) do
37
+ darcs("add --recursive .")
38
+
39
+ logfile = Tempfile.new("darcs_logfile")
40
+ logfile.print("something nice\n")
41
+ logfile.print(message + "\n")
42
+ logfile.close
43
+
44
+ darcs("record --all --logfile #{PathConverter.filepath_to_nativepath(logfile.path, false)}")
45
+ end
46
+ end
47
+
48
+ def commit(message)
49
+ logfile = Tempfile.new("darcs_logfile")
50
+ logfile.print("something nice\n")
51
+ logfile.print(message + "\n")
52
+ logfile.close
53
+
54
+ with_working_dir(@checkout_dir) do
55
+ darcs("record --all --logfile #{PathConverter.filepath_to_nativepath(logfile.path, false)}")
56
+ end
57
+ end
58
+
59
+ def add(relative_filename)
60
+ with_working_dir(@checkout_dir) do
61
+ darcs("add #{relative_filename}")
62
+ end
63
+ end
64
+
65
+ def checked_out?
66
+ File.exists?("#{@checkout_dir}/_darcs")
67
+ end
68
+
69
+ def uptodate?(from_identifier)
70
+ if (!checked_out?(@checkout_dir))
71
+ false
72
+ else
73
+ with_working_dir(@checkout_dir) do
74
+ darcs("pull --dry-run #{@dir}") do |io|
75
+ io.each_line do |line|
76
+ if (line =~ /No remote changes to pull in!/)
77
+ true
78
+ else
79
+ false
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def revisions(from_identifier, to_identifier=Time.infinity)
88
+ from_identifier = Time.epoch if from_identifier.nil?
89
+ to_identifier = Time.infinity if to_identifier.nil?
90
+ with_working_dir(@checkout_dir) do
91
+ darcs("changes --summary --xml-output") do |stdout|
92
+ DarcsLogParser.new.parse_revisions(stdout, from_identifier, to_identifier)
93
+ end
94
+ end
95
+ end
96
+
97
+ def supports_trigger?
98
+ true
99
+ end
100
+
101
+ protected
102
+
103
+ def checkout_silent(to_identifier) # :yield: file
104
+ with_working_dir(File.dirname(checkout_dir)) do
105
+ darcs("get --repo-name #{File.basename(checkout_dir)} #{@dir}")
106
+ end
107
+ end
108
+
109
+ def ignore_paths
110
+ return [/_darcs/]
111
+ end
112
+
113
+ private
114
+
115
+ def darcs(darcs_cmd, options={}, &proc)
116
+ cmd = "darcs #{darcs_cmd}"
117
+ execute(cmd, options, &proc)
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,65 @@
1
+ require 'rscm'
2
+ require 'time'
3
+ require 'stringio'
4
+ require 'rexml/document'
5
+
6
+ module RSCM
7
+ class DarcsLogParser
8
+ def parse_revisions(io, from_identifier=Time.epoch, to_identifier=Time.infinity)
9
+ revisions = Revisions.new
10
+
11
+ doc = REXML::Document.new(io)
12
+
13
+ path_revisions = {}
14
+ doc.elements.each("//patch") do |element|
15
+ revision = parse_revision(element.to_s, path_revisions)
16
+ if ((from_identifier <= revision.time) && (revision.time <= to_identifier))
17
+ revisions.add(revision)
18
+ end
19
+ end
20
+
21
+ revisions.each do |revision|
22
+ revision.each do |change|
23
+ current_index = path_revisions[change.path].index(change.native_revision_identifier)
24
+ change.previous_native_revision_identifier = path_revisions[change.path][current_index + 1]
25
+ end
26
+ end
27
+
28
+ revisions
29
+ end
30
+
31
+ def parse_revision(revision_io, path_revisions)
32
+ revision = Revision.new
33
+
34
+ doc = REXML::Document.new(revision_io)
35
+
36
+ doc.elements.each("patch") do |element|
37
+ revision.identifier = element.attributes['hash']
38
+ revision.developer = element.attributes['author']
39
+ revision.time = Time.parse(element.attributes['local_date'])
40
+ revision.message = element.elements["comment"].text
41
+ revision.message.lstrip!
42
+ revision.message.rstrip!
43
+
44
+ element.elements["summary"].elements.each("add_file") do |file|
45
+ add_changes(revision, file.text.strip, RevisionFile::ADDED, path_revisions)
46
+ end
47
+ element.elements["summary"].elements.each("modify_file") do |file|
48
+ add_changes(revision, file.text.strip, RevisionFile::MODIFIED, path_revisions)
49
+ end
50
+ end
51
+
52
+ revision
53
+ end
54
+
55
+ private
56
+
57
+ def add_changes(revision, path, state, path_revisions)
58
+ revision << RevisionFile.new(path, state, revision.developer, nil, revision.identifier, revision.time)
59
+
60
+ path_revisions[path] ||= []
61
+ path_revisions[path] << revision.identifier
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,338 @@
1
+ require 'rscm/line_editor'
2
+ require 'fileutils'
3
+ require 'rscm'
4
+
5
+ module RSCM
6
+ class Monotone < Base
7
+ attr_accessor :db_file
8
+ attr_accessor :branch
9
+ attr_accessor :key
10
+ attr_accessor :passphrase
11
+ attr_accessor :keys_file
12
+ attr_accessor :server
13
+
14
+ def initialize(branch=nil, key=nil, passphrase=nil, keys_file=nil, server=nil, central_checkout_dir=nil)
15
+ @branch = branch
16
+ @key = key
17
+ @passphrase = passphrase
18
+ @keys_file = keys_file
19
+ @server = server
20
+ @central_checkout_dir = File.expand_path(central_checkout_dir) unless central_checkout_dir.nil?
21
+ end
22
+
23
+ def add(relative_filename)
24
+ db = db(@checkout_dir)
25
+ with_working_dir(@checkout_dir) do
26
+ monotone("add #{relative_filename}", db)
27
+ end
28
+ end
29
+
30
+ def move(relative_src, relative_dest)
31
+ with_working_dir(@checkout_dir) do
32
+ monotone("rename #{relative_src} #{relative_dest}", db)
33
+ FileUtils.mv(relative_src, relative_dest)
34
+ end
35
+ end
36
+
37
+ def can_create_central?
38
+ @server == "localhost" && !@central_checkout_dir.nil?
39
+ end
40
+
41
+ def central_exists?
42
+ @central_checkout_dir && @serve_pid
43
+ end
44
+
45
+ def create_central
46
+ init(@central_checkout_dir)
47
+ # create empty working copy
48
+ dir = PathConverter.filepath_to_nativepath(@central_checkout_dir, false)
49
+ # set up a working copy
50
+ monotone("setup #{dir}")
51
+ start_serve
52
+ end
53
+
54
+ def start_serve
55
+ mode = File::CREAT|File::WRONLY
56
+ if File.exist?(rcfile)
57
+ mode = File::APPEND|File::WRONLY
58
+ end
59
+
60
+ begin
61
+ File.open(rcfile, mode) do |file|
62
+ file.puts("function get_netsync_anonymous_read_permitted(collection)")
63
+ file.puts(" return true")
64
+ file.puts("end")
65
+ end
66
+ rescue => e
67
+ puts e.message
68
+ puts e.backtrace.join("\n")
69
+ raise "Didn't have permission to write to #{rcfile}."
70
+ end
71
+
72
+ @serve_pid = fork do
73
+ #Signal.trap("HUP") { puts "Monotone server shutting down..."; exit }
74
+ monotone("serve --rcfile=\"#{rcfile}\" #{@server} #{@branch}", db(@central_checkout_dir)) do |io|
75
+ puts "PASSPHRASE: #{@passphrase}"
76
+ io.puts(@passphrase)
77
+ io.close_write
78
+ end
79
+ end
80
+ Process.detach(@serve_pid)
81
+ end
82
+
83
+ def stop_serve
84
+ Process.kill("HUP", @serve_pid) if @serve_pid
85
+ Process.waitpid2(@serve_pid) if @serve_pid
86
+ @serve_pid = nil
87
+ end
88
+
89
+ def destroy_central
90
+ stop_serve
91
+ FileUtils.rm_rf(@central_checkout_dir) if File.exist?(@central_checkout_dir)
92
+ FileUtils.rm(db(@central_checkout_dir)) if File.exist?(db(@central_checkout_dir))
93
+ puts "Destroyed Monotone server"
94
+ end
95
+
96
+ def transactional?
97
+ true
98
+ end
99
+
100
+ def import_central(dir, message)
101
+ FileUtils.cp_r(Dir["#{dir}/*"], @central_checkout_dir)
102
+ with_working_dir(@central_checkout_dir) do
103
+ monotone("add .")
104
+ commit_in_dir(message, @central_checkout_dir)
105
+ end
106
+ end
107
+
108
+ def checked_out?
109
+ mt = File.expand_path("#{@checkout_dir}/MT")
110
+ File.exists?(mt)
111
+ end
112
+
113
+ def uptodate?(identifier=nil)
114
+ if (!checked_out?)
115
+ false
116
+ else
117
+ pull
118
+
119
+ rev = identifier ? identifier : head_revision
120
+ local_revision == rev
121
+ end
122
+ end
123
+
124
+ def revisions(from_identifier, to_identifier=Time.infinity)
125
+ checkout(to_identifier)
126
+ to_identifier = Time.infinity if to_identifier.nil?
127
+ with_working_dir(checkout_dir) do
128
+ monotone("log") do |stdout|
129
+ MonotoneLogParser.new.parse_revisions(stdout, from_identifier, to_identifier)
130
+ end
131
+ end
132
+ end
133
+
134
+ def commit(message)
135
+ commit_in_dir(message, @checkout_dir)
136
+ with_working_dir(@checkout_dir) do
137
+ monotone("push #{@server} #{@branch}") do |io|
138
+ io.puts(@passphrase)
139
+ io.close_write
140
+ io.read
141
+ end
142
+ end
143
+ end
144
+
145
+ def supports_trigger?
146
+ true
147
+ end
148
+
149
+ def trigger_mechanism
150
+ "MT/monotonerc"
151
+ end
152
+
153
+ # http://www.venge.net/monotone/monotone.html#Hook-Reference
154
+ def install_trigger(trigger_command, install_dir)
155
+ stop_serve
156
+ if (WINDOWS)
157
+ install_win_trigger(trigger_comand, install_dir)
158
+ else
159
+ install_unix_trigger(trigger_command, install_dir)
160
+ end
161
+ start_serve
162
+ end
163
+
164
+ def trigger_installed?(trigger_command, install_dir)
165
+ File.exist?(rcfile)
166
+ end
167
+
168
+ def uninstall_trigger(trigger_command, install_dir)
169
+ stop_serve
170
+ File.delete(rcfile)
171
+ start_serve
172
+ end
173
+
174
+ def diff(change, &block)
175
+ checkout(change.revision)
176
+ with_working_dir(@checkout_dir) do
177
+ monotone("diff --revision=#{change.previous_native_revision_identifier} #{change.path}") do |stdout|
178
+ yield stdout
179
+ end
180
+ end
181
+ end
182
+
183
+ protected
184
+
185
+ # Checks out silently. Called by superclass' checkout.
186
+ def checkout_silent(to_identifier)
187
+ # raise "Monotone doesn't support checkout to time. Please use identifiers instead." if to_identifier.is_a?(Time)
188
+ db_file = db(@checkout_dir)
189
+ if(!File.exist?(db_file))
190
+ init(@checkout_dir)
191
+ end
192
+
193
+ pull
194
+ checked_out = checked_out?
195
+
196
+ with_working_dir(@checkout_dir) do
197
+ monotone("checkout .", db_file, @branch) unless checked_out
198
+
199
+ selector = expand_selector(to_identifier)
200
+ monotone("update #{selector}", db_file)
201
+ end
202
+ end
203
+
204
+ # Administrative files that should be ignored when counting files.
205
+ def ignore_paths
206
+ [/MT/, /\.mt-attrs/]
207
+ end
208
+
209
+ private
210
+
211
+ def commit_in_dir(message, dir)
212
+ db_file = db(dir)
213
+ with_working_dir(dir) do
214
+ monotone("commit --message='#{message}'", db_file, @branch, @key) do |io|
215
+ io.puts(@passphrase)
216
+ io.close_write
217
+ io.read
218
+ end
219
+ end
220
+ end
221
+
222
+ def pull
223
+ db_file = db(@checkout_dir)
224
+ with_working_dir(@checkout_dir) do
225
+ # pull from the "central" server
226
+ if(@server)
227
+ monotone("pull #{@server} #{@branch}", db_file) do |io|
228
+ io.puts(@passphrase)
229
+ io.close_write
230
+ io.read
231
+ end
232
+ end
233
+ end
234
+ end
235
+
236
+ def db(checkout_dir)
237
+ PathConverter.filepath_to_nativepath(checkout_dir + ".db", false)
238
+ end
239
+
240
+ # Initialises a monotone database
241
+ #
242
+ def init(dir)
243
+ dir = File.expand_path(dir)
244
+ db_file = db(dir)
245
+ raise "Database #{db_file} already exists" if File.exist?(db_file)
246
+ FileUtils.mkdir_p(File.dirname(db_file))
247
+ # create database
248
+ monotone("db init", db_file)
249
+ # TODO: do a genkey
250
+ monotone("read", db_file) do |io|
251
+ io.write(File.open(@keys_file).read)
252
+ io.close_write
253
+ end
254
+ end
255
+
256
+ def install_unix_trigger(trigger_command, install_dir)
257
+ mode = File::CREAT|File::WRONLY
258
+ if File.exist?(rcfile)
259
+ mode = File::APPEND|File::WRONLY
260
+ end
261
+
262
+ begin
263
+ File.open(rcfile, mode) do |file|
264
+ file.puts("function note_commit(new_id, certs)")
265
+ execstr = "\"" + trigger_command.split.join("\",\"") + "\""
266
+ file.puts(" execute(#{execstr})")
267
+ file.puts("end")
268
+ end
269
+ rescue => e
270
+ puts e.message
271
+ puts e.backtrace.join("\n")
272
+ raise "Didn't have permission to write to #{rcfile}."
273
+ end
274
+
275
+ # push to the "central" server
276
+ # monotone("push #{@server} #{@branch}", db(@central_checkout_dir))
277
+ end
278
+
279
+ def rcfile
280
+ "#{@central_checkout_dir}/MT/monotonerc"
281
+ end
282
+
283
+ def local_revision
284
+ local_revision = nil
285
+ rev_file = File.expand_path("#{checkout_dir}/MT/revision")
286
+ local_revision = File.open(rev_file).read.strip
287
+ local_revision
288
+ end
289
+
290
+ def head_revision
291
+ # FIXME: this will grab last head if heads are not merged.
292
+ head_revision = nil
293
+ monotone("heads", db(@checkout_dir), @branch) do |stdout|
294
+ stdout.each_line do |line|
295
+ next if (line =~ /^monotone:/)
296
+ head_revision = line.split(" ")[0]
297
+ end
298
+ end
299
+ head_revision
300
+ end
301
+
302
+ # See http://www.venge.net/monotone/monotone.html#Selectors
303
+ # Also see docs for expand_selector in the same document
304
+ # Dates are formatted with strftime-style %F, which is of style 2005-28-02,
305
+ # which is very coarse grained. Date identifiers are therefore discouraged.
306
+ def expand_selector(identifier)
307
+ if(identifier.is_a?(Time))
308
+ # Won't work:
309
+ # "d:#{identifier.strftime('%Y-%m-%d')}"
310
+ ""
311
+ else
312
+ "i:#{identifier}"
313
+ end
314
+ end
315
+
316
+ def monotone(monotone_cmd, db_file=nil, branch=nil, key=nil)
317
+ db_opt = db_file ? "--db=\"#{db_file}\"" : ""
318
+ branch_opt = branch ? "--branch=\"#{branch}\"" : ""
319
+ key_opt = key ? "--key=\"#{key}\"" : ""
320
+ rcfile_opt = @rcfile ? "--rcfile=\"#{@rcfile}\"" : ""
321
+ cmd = "monotone #{db_opt} #{branch_opt} #{key_opt} #{rcfile_opt} #{monotone_cmd}"
322
+ Better.popen(cmd, "r+") do |io|
323
+ if(block_given?)
324
+ return(yield(io))
325
+ else
326
+ # just read stdout so we can exit
327
+ io.read
328
+ end
329
+ end
330
+ end
331
+
332
+ def monotone_date(time)
333
+ return nil unless time
334
+ time.utc.strftime("%Y-%m-%dT%H:%M:%S")
335
+ end
336
+
337
+ end
338
+ end