whistle 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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