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,14 @@
1
+ require 'rscm/revision_poller'
2
+ require 'rscm/path_converter'
3
+ require 'rscm/difftool'
4
+ require 'rscm/platform'
5
+ require 'rscm/command_line'
6
+ require 'rscm/base'
7
+ require 'rscm/revision'
8
+ require 'rscm/revision_file'
9
+ require 'rscm/time_ext'
10
+ # Load all sources under scm
11
+ Dir[File.dirname(__FILE__) + "/rscm/scm/*.rb"].each do |src|
12
+ require src
13
+ end
14
+
@@ -0,0 +1,35 @@
1
+ module RSCM
2
+
3
+ # NOTE: It is recommended to use the Parser class in parser.rb
4
+ # as a basis for new SCM parsers
5
+ #
6
+ # Some utilities for log-parsers
7
+ # TODO: make this a module and remove the attr_reader
8
+ class AbstractLogParser
9
+
10
+ def initialize(io)
11
+ @io = io
12
+ end
13
+
14
+ def read_until_matching_line(regexp)
15
+ return nil if @io.eof?
16
+ result = ""
17
+ @io.each_line do |line|
18
+ line.gsub!(/\r\n$/, "\n")
19
+ break if line =~ regexp
20
+ result << line
21
+ end
22
+ if result.strip == ""
23
+ read_until_matching_line(regexp)
24
+ else
25
+ result
26
+ end
27
+ end
28
+
29
+ def convert_all_slashes_to_forward_slashes(file)
30
+ file.gsub(/\\/, "/")
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,289 @@
1
+ require 'fileutils'
2
+ require 'rscm/revision'
3
+ require 'rscm/path_converter'
4
+
5
+ module RSCM
6
+ # This class defines the RSCM API, which offers access to an SCM working copy
7
+ # as well as a 'central' repository.
8
+ #
9
+ # Concrete subclasses of this class (concrete adapters) implement the integration
10
+ # with the respective SCMs.
11
+ #
12
+ # Most of the methods take an optional +options+ Hash (named parameters), allowing
13
+ # the following options:
14
+ #
15
+ # * <tt>:stdout</tt>: Path to file name where stdout of SCM operations are written.
16
+ # * <tt>:stdout</tt>: Path to file name where stderr of SCM operations are written.
17
+ #
18
+ # In stead of specifying the +options+ parameters for every API method, it's possible
19
+ # to assign default options via the +default_options+ attribute.
20
+ #
21
+ # Some of the methods in this API use +from_identifier+ and +to_identifier+.
22
+ # These identifiers can be either a UTC Time (according to the SCM's clock)
23
+ # or a String or Integer representing a label/revision
24
+ # (according to the SCM's native label/revision scheme).
25
+ #
26
+ # If +from_identifier+ or +to_identifier+ are +nil+ they should respectively default to
27
+ # Time.epoch or Time.infinite.
28
+ #
29
+ class Base
30
+ include RevisionPoller
31
+
32
+ attr_writer :default_options
33
+ attr_writer :store_revisions_command
34
+
35
+ def default_options
36
+ @default_options ||= {}
37
+ end
38
+
39
+ # Returns true if the underlying SCM tool is available on this system.
40
+ def available?
41
+ raise NotImplementedError
42
+ end
43
+
44
+ # Transforms +raw_identifier+ into the native rype used for revisions.
45
+ def to_identifier(raw_identifier)
46
+ raw_identifier.to_s
47
+ end
48
+
49
+ # Sets the checkout dir (working copy). Should be set prior to most other method
50
+ # invocations (depending on the implementation).
51
+ def checkout_dir=(dir)
52
+ @checkout_dir = PathConverter.filepath_to_nativepath(dir, false)
53
+ end
54
+
55
+ # Gets the working copy directory.
56
+ def checkout_dir
57
+ @checkout_dir
58
+ end
59
+
60
+ def to_yaml_properties #:nodoc:
61
+ props = instance_variables
62
+ props.delete("@checkout_dir")
63
+ props.delete("@default_options")
64
+ props.sort!
65
+ end
66
+
67
+ # Destroys the working copy
68
+ def destroy_working_copy(options={})
69
+ FileUtils.rm_rf(checkout_dir) unless checkout_dir.nil?
70
+ end
71
+
72
+ # Whether or not the SCM represented by this instance exists.
73
+ def central_exists?
74
+ # The default implementation assumes yes - override if it can be
75
+ # determined programmatically.
76
+ true
77
+ end
78
+
79
+ # Whether or not this SCM is transactional (atomic).
80
+ def transactional?
81
+ false
82
+ end
83
+ alias :atomic? :transactional?
84
+
85
+ # Creates a new 'central' repository. This is intended only for creation of 'central'
86
+ # repositories (not for working copies). You shouldn't have to call this method if a central repository
87
+ # already exists. This method is used primarily for testing of RSCM, but can also
88
+ # be used if you *really* want to use RSCM to create a central repository.
89
+ #
90
+ # This method should throw an exception if the repository cannot be created (for
91
+ # example if the repository is 'remote' or if it already exists).
92
+ #
93
+ def create_central(options={})
94
+ raise NotImplementedError
95
+ end
96
+
97
+ # Destroys the central repository. Shuts down any server processes and deletes the repository.
98
+ # WARNING: calling this may result in loss of data. Only call this if you really want to wipe
99
+ # it out for good!
100
+ def destroy_central
101
+ raise NotImplementedError
102
+ end
103
+
104
+ # Whether a repository can be created.
105
+ def can_create_central?
106
+ false
107
+ end
108
+
109
+ # Adds +relative_filename+ to the working copy.
110
+ def add(relative_filename, options={})
111
+ raise NotImplementedError
112
+ end
113
+
114
+ # Schedules a move of +relative_src+ to +relative_dest+
115
+ # Should not take effect in the central repository until
116
+ # +commit+ is invoked.
117
+ def move(relative_src, relative_dest, options={})
118
+ raise NotImplementedError
119
+ end
120
+
121
+ # Recursively imports files from <tt>:dir</tt> into the central scm,
122
+ # using commit message <tt>:message</tt>
123
+ def import_central(options)
124
+ raise NotImplementedError
125
+ end
126
+
127
+ # Open a file for edit - required by scms that check out files in read-only mode e.g. perforce
128
+ def edit(file, options={})
129
+ end
130
+
131
+ # Commit (check in) modified files.
132
+ def commit(message, options={})
133
+ raise NotImplementedError
134
+ end
135
+
136
+ # Checks out or updates contents from a central SCM to +checkout_dir+ - a local working copy.
137
+ # If this is a distributed SCM, this method should create a 'working copy' repository
138
+ # if one doesn't already exist. Then the contents of the central SCM should be pulled into
139
+ # the working copy.
140
+ #
141
+ # The +to_identifier+ parameter may be optionally specified to obtain files up to a
142
+ # particular time or label. +to_identifier+ should either be a Time (in UTC - according to
143
+ # the clock on the SCM machine) or a String - reprsenting a label or revision.
144
+ #
145
+ # This method will yield the relative file name of each checked out file, and also return
146
+ # them in an array. Only files, not directories, should be yielded/returned.
147
+ #
148
+ # This method should be overridden for SCMs that are able to yield checkouts as they happen.
149
+ # For some SCMs this is not possible, or at least very hard. In that case, just override
150
+ # the checkout_silent method instead of this method (should be protected).
151
+ #
152
+ def checkout(to_identifier=Time.infinity, options={}) # :yield: file
153
+ to_identifier = Time.infinity if to_identifier.nil?
154
+
155
+ before = checked_out_files
156
+ # We expect subclasses to implement this as a protected method (unless this whole method is overridden).
157
+ checkout_silent(to_identifier, options)
158
+ after = checked_out_files
159
+
160
+ (after - before).sort!
161
+ end
162
+
163
+ def checked_out_files
164
+ raise "checkout_dir not set" if @checkout_dir.nil?
165
+
166
+ files = Dir["#{@checkout_dir}/**/*"]
167
+ files.delete_if{|file| File.directory?(file)}
168
+ ignore_paths.each do |regex|
169
+ files.delete_if{|file| file =~ regex}
170
+ end
171
+ dir = File.expand_path(@checkout_dir)
172
+ files.collect{|file| File.expand_path(file)[dir.length+1..-1]}
173
+ end
174
+
175
+ # Returns a Revisions object for the interval specified by +from_identifier+ (exclusive, i.e. after)
176
+ # and optionally +:to_identifier+ (exclusive too). If +relative_path+ is specified, the result will only contain
177
+ # revisions pertaining to that path.
178
+ #
179
+ # For example, revisions(223, 229) should return revisions 224..228
180
+ def revisions(from_identifier, options={})
181
+ raise NotImplementedError
182
+ end
183
+
184
+ # Opens a readonly IO to a file at +path+
185
+ def open(path, native_revision_identifier, options={}, &block) #:yield: io
186
+ raise NotImplementedError
187
+ end
188
+
189
+ # Whether the working copy is in synch with the central
190
+ # repository's revision/time identified by +identifier+.
191
+ # If +identifier+ is nil, 'HEAD' of repository should be assumed.
192
+ #
193
+ def uptodate?(identifier)
194
+ raise NotImplementedError
195
+ end
196
+
197
+ # Whether the project is checked out from the central repository or not.
198
+ # Subclasses should override this to check for SCM-specific administrative
199
+ # files if appliccable
200
+ def checked_out?
201
+ File.exists?(@checkout_dir)
202
+ end
203
+
204
+ # Whether triggers are supported by this SCM. A trigger is a command that can be executed
205
+ # upon a completed commit to the SCM.
206
+ def supports_trigger?
207
+ # The default implementation assumes no - override if it can be
208
+ # determined programmatically.
209
+ false
210
+ end
211
+ alias :can_install_trigger? :supports_trigger?
212
+
213
+ # Descriptive name of the trigger mechanism
214
+ def trigger_mechanism
215
+ raise NotImplementedError
216
+ end
217
+
218
+ # Installs +trigger_command+ in the SCM.
219
+ # The +install_dir+ parameter should be an empty local
220
+ # directory that the SCM can use for temporary files
221
+ # if necessary (CVS needs this to check out its administrative files).
222
+ # Most implementations will ignore this parameter.
223
+ #
224
+ def install_trigger(trigger_command, install_dir)
225
+ raise NotImplementedError
226
+ end
227
+
228
+ # Uninstalls +trigger_command+ from the SCM.
229
+ #
230
+ def uninstall_trigger(trigger_command, install_dir)
231
+ raise NotImplementedError
232
+ end
233
+
234
+ # Whether the command denoted by +trigger_command+ is installed in the SCM.
235
+ #
236
+ def trigger_installed?(trigger_command, install_dir)
237
+ raise NotImplementedError
238
+ end
239
+
240
+ # The command line to run in order to check out a fresh working copy.
241
+ #
242
+ def checkout_commandline(to_identifier=Time.infinity)
243
+ raise NotImplementedError
244
+ end
245
+
246
+ # The command line to run in order to update a working copy.
247
+ #
248
+ def update_commandline(to_identifier=Time.infinity)
249
+ raise NotImplementedError
250
+ end
251
+
252
+ # Yields an IO containing the unified diff of the change.
253
+ # Also see RevisionFile#diff
254
+ def diff(path, from, to, options={}, &block)
255
+ raise NotImplementedError
256
+ end
257
+
258
+ def ==(other_scm)
259
+ return false if self.class != other_scm.class
260
+ self.instance_variables.each do |var|
261
+ return false if self.instance_eval(var) != other_scm.instance_eval(var)
262
+ end
263
+ true
264
+ end
265
+
266
+ # Whether or not to store the revision command in the Revisions instance returned by <tt>revisions</tt>
267
+ def store_revisions_command?; @store_revisions_command.nil? ? true : @store_revisions_command; end
268
+
269
+ protected
270
+
271
+ # Directory where commands must be run
272
+ def cmd_dir
273
+ nil
274
+ end
275
+
276
+ # Wrapper for CommandLine.execute that provides default values for
277
+ # dir plus any options set in default_options (typically stdout and stderr).
278
+ def execute(cmd, options={}, &proc)
279
+ options = {:dir => cmd_dir}.merge(default_options).merge(options)
280
+ begin
281
+ CommandLine.execute(cmd, options, &proc)
282
+ rescue CommandLine::OptionError => e
283
+ e.message += "\nEither specify default_options on the scm object, or pass the required options to the method"
284
+ raise e
285
+ end
286
+ end
287
+
288
+ end
289
+ end
@@ -0,0 +1,146 @@
1
+ require 'rscm/platform'
2
+
3
+ module RSCM
4
+ module CommandLine
5
+ QUOTE_REPLACEMENT = (Platform.family == "mswin32") ? "\"" : "\\\""
6
+ LESS_THAN_REPLACEMENT = (Platform.family == "mswin32") ? "<" : "\\<"
7
+ class OptionError < StandardError; end
8
+ class ExecutionError < StandardError
9
+ attr_reader :cmd, :dir, :exitstatus, :stderr
10
+ def initialize(cmd, full_cmd, dir, exitstatus, stderr)
11
+ @cmd, @full_cmd, @dir, @exitstatus, @stderr = cmd, full_cmd, dir, exitstatus, stderr
12
+ end
13
+ def to_s
14
+ "\ndir : #{@dir}\n" +
15
+ "command : #{@cmd}\n" +
16
+ "executed command : #{@full_cmd}\n" +
17
+ "exitstatus: #{@exitstatus}\n" +
18
+ "STDERR TAIL START\n#{@stderr}\nSTDERR TAIL END\n"
19
+ end
20
+ end
21
+
22
+ # Executes +cmd+.
23
+ # If the +:stdout+ and +:stderr+ options are specified, a line consisting
24
+ # of a prompt (including +cmd+) will be appended to the respective output streams will be appended
25
+ # to those files, followed by the output itself. Example:
26
+ #
27
+ # CommandLine.execute("echo hello world", {:stdout => "stdout.log", :stderr => "stderr.log"})
28
+ #
29
+ # will result in the following being written to stdout.log:
30
+ #
31
+ # /Users/aslakhellesoy/scm/buildpatterns/repos/damagecontrol/trunk aslakhellesoy$ echo hello world
32
+ # hello world
33
+ #
34
+ # -and to stderr.log:
35
+ # /Users/aslakhellesoy/scm/buildpatterns/repos/damagecontrol/trunk aslakhellesoy$ echo hello world
36
+ #
37
+ # If a block is passed, the stdout io will be yielded to it (as with IO.popen). In this case the output
38
+ # will not be written to the stdout file (even if it's specified):
39
+ #
40
+ # /Users/aslakhellesoy/scm/buildpatterns/repos/damagecontrol/trunk aslakhellesoy$ echo hello world
41
+ # [output captured and therefore not logged]
42
+ #
43
+ # If the exitstatus of the command is different from the value specified by the +:exitstatus+ option
44
+ # (which defaults to 0) then an ExecutionError is raised, its message containing the last 400 bytes of stderr
45
+ # (provided +:stderr+ was specified)
46
+ #
47
+ # You can also specify the +:dir+ option, which will cause the command to be executed in that directory
48
+ # (default is current directory).
49
+ #
50
+ # You can also specify a hash of environment variables in +:env+, which will add additional environment variables
51
+ # to the default environment.
52
+ #
53
+ # Finally, you can specify several commands within one by separating them with '&&' (as you would in a shell).
54
+ # This will result in several lines to be appended to the log (as if you had executed the commands separately).
55
+ #
56
+ # See the unit test for more examples.
57
+ def execute(cmd, options={}, &proc)
58
+ raise "Can't have newline in cmd" if cmd =~ /\n/
59
+ options = {
60
+ :dir => Dir.pwd,
61
+ :env => {},
62
+ :mode => 'r',
63
+ :exitstatus => 0
64
+ }.merge(options)
65
+
66
+ options[:stdout] = File.expand_path(options[:stdout]) if options[:stdout]
67
+ options[:stderr] = File.expand_path(options[:stderr]) if options[:stderr]
68
+
69
+ if options[:dir].nil?
70
+ e(cmd, options, &proc)
71
+ else
72
+ Dir.chdir(options[:dir]) do
73
+ e(cmd, options, &proc)
74
+ end
75
+ end
76
+ end
77
+ module_function :execute
78
+
79
+ private
80
+
81
+ def full_cmd(cmd, options, &proc)
82
+ commands = cmd.split("&&").collect{|c| c.strip}
83
+ stdout_opt = options[:stdout] ? ">> #{options[:stdout]}" : ""
84
+ stderr_opt = options[:stderr] ? "2>> #{options[:stderr]}" : ""
85
+ capture_info_command = (block_given? && options[:stdout])? "echo [output captured and therefore not logged] >> #{options[:stdout]} && " : ""
86
+
87
+ full_cmd = commands.collect do |c|
88
+ escaped_command = c.gsub(/"/, QUOTE_REPLACEMENT).gsub(/</, LESS_THAN_REPLACEMENT)
89
+ stdout_prompt_command = options[:stdout] ? "echo #{RSCM::Platform.prompt} #{escaped_command} >> #{options[:stdout]} && " : ""
90
+ stderr_prompt_command = options[:stderr] ? "echo #{RSCM::Platform.prompt} #{escaped_command} >> #{options[:stderr]} && " : ""
91
+ redirected_command = block_given? ? "#{c} #{stderr_opt}" : "#{c} #{stdout_opt} #{stderr_opt}"
92
+
93
+ stdout_prompt_command + capture_info_command + stderr_prompt_command + redirected_command
94
+ end.join(" && ")
95
+ end
96
+ module_function :full_cmd
97
+
98
+ def verify_exit_code(cmd, full_cmd, options)
99
+ if($?.exitstatus != options[:exitstatus])
100
+ error_message = "#{options[:stderr]} doesn't exist"
101
+ if options[:stderr] && File.exist?(options[:stderr])
102
+ File.open(options[:stderr]) do |errio|
103
+ begin
104
+ errio.seek(-1200, IO::SEEK_END)
105
+ rescue Errno::EINVAL
106
+ # ignore - it just means we didn't have 400 bytes.
107
+ end
108
+ error_message = errio.read
109
+ end
110
+ end
111
+ raise ExecutionError.new(cmd, full_cmd, options[:dir] || Dir.pwd, $?.exitstatus, error_message)
112
+ end
113
+ end
114
+ module_function :verify_exit_code
115
+
116
+ def e(cmd, options, &proc)
117
+ full_cmd = full_cmd(cmd, options, &proc)
118
+
119
+ options[:env].each{|k,v| ENV[k]=v}
120
+ begin
121
+ STDOUT.puts "#{RSCM::Platform.prompt} #{cmd}" if options[:stdout].nil?
122
+ IO.popen(full_cmd, options[:mode]) do |io|
123
+ if(block_given?)
124
+ proc.call(io)
125
+ else
126
+ io.each_line do |line|
127
+ STDOUT.puts line if options[:stdout].nil?
128
+ end
129
+ end
130
+ end
131
+ rescue Errno::ENOENT => e
132
+ unless options[:stderr].nil?
133
+ File.open(options[:stderr], "a") {|io| io.write(e.message)}
134
+ else
135
+ STDERR.puts e.message
136
+ STDERR.puts e.backtrace.join("\n")
137
+ end
138
+ raise ExecutionError.new(cmd, full_cmd, options[:dir] || Dir.pwd, nil, e.message)
139
+ ensure
140
+ verify_exit_code(cmd, full_cmd, options)
141
+ end
142
+ end
143
+ module_function :e
144
+
145
+ end
146
+ end