Class: VCSConnector
- Inherits:
-
Object
- Object
- VCSConnector
- Defined in:
- lib/vcseif/vcs_connector.rb
Constant Summary collapse
- VERSION =
VCSEIF::Version
- PLUGIN_SPEC_PATTERN =
Regexp.compile('^(?<plugin_class>\w+)\s*\((?<plugin_config>[^\)]*)\)\s*$')
- PLAIN_PLUGIN_SPEC_PATTERN =
Regexp.compile('(?<plugin_class>\w+)\s*$')
- RALLY_TIMESTAMP =
"%Y-%m-%dT%H:%M:%S.%L %Z"
- ISO8601_TIMESTAMP =
"%Y-%m-%dT%H:%M:%S"
- @@transformable_attributes =
['Author']
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#log ⇒ Object
readonly
Returns the value of attribute log.
-
#rally_conf ⇒ Object
readonly
Returns the value of attribute rally_conf.
-
#rally_conn ⇒ Object
readonly
Returns the value of attribute rally_conn.
-
#rally_conn_class ⇒ Object
readonly
Returns the value of attribute rally_conn_class.
-
#svc_conf ⇒ Object
readonly
Returns the value of attribute svc_conf.
-
#transforms ⇒ Object
readonly
Returns the value of attribute transforms.
-
#txfm_conf ⇒ Object
readonly
Returns the value of attribute txfm_conf.
-
#vcs_conf ⇒ Object
readonly
Returns the value of attribute vcs_conf.
-
#vcs_conn ⇒ Object
readonly
Returns the value of attribute vcs_conn.
-
#vcs_conn_class ⇒ Object
readonly
Returns the value of attribute vcs_conn_class.
-
#vcs_name ⇒ Object
readonly
Returns the value of attribute vcs_name.
Instance Method Summary collapse
- #collect_files_and_links(revnum, file_list, operation) ⇒ Object
- #establishConnections ⇒ Object
- #get_config_lookback(config) ⇒ Object
- #getRefTimes(last_commit_timestamp) ⇒ Object
- #identifyUnrecordedChangesets(rally_changesets, vcs_changesets, ref_time) ⇒ Object
-
#initialize(config, logger) ⇒ VCSConnector
constructor
A new instance of VCSConnector.
- #internalizeConfig(config) ⇒ Object
- #postBatch(extension, status, repo_changesets) ⇒ Object
- #preBatch(extension) ⇒ Object
-
#reflectChangesetsInRally(last_commit_timestamp) ⇒ Object
last_commit_timestamp is an instance of Time.
- #run(last_commit_timestamp, extension) ⇒ Object
- #transformEligibleAttributes(changeset) ⇒ Object
- #validate ⇒ Object
Constructor Details
#initialize(config, logger) ⇒ VCSConnector
Returns a new instance of VCSConnector.
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 |
# File 'lib/vcseif/vcs_connector.rb', line 40 def initialize(config, logger) @config = config @log = logger conn_sections = [] begin non_conn_sections = ['Services', 'Transforms'] conn_sections = config.topLevels.select {|section| !non_conn_sections.include?(section)} if conn_sections.length != 2 problem = 'Config does not contain two connection sections' confex = VCSEIF_Exceptions::ConfigurationError.new(problem) raise confex, problem end rescue Exception => ex problem = 'Config file lacks sufficient information for VCSConnector operation' confex = VCSEIF_Exceptions::ConfigurationError.new(problem) raise confex, problem end @rally_conn = nil @vcs_conn = nil @vcs_name = conn_sections.select {|section_name| section_name !~ /^Rally/}.first @log.info("Rally VCS Connector for %s, version %s" % [@vcs_name, VERSION]) @log.info("Ruby platform: #{RUBY_PLATFORM}") @log.info("Ruby version: #{RUBY_VERSION}") internalizeConfig(config) rally_conn_class_name = config.connectionClassName('Rally') vcs_conn_class_name = config.connectionClassName(@vcs_name) @log.debug("Loading #{rally_conn_class_name} class") begin @rally_conn_class = ClassLoader.loadConnectionClass('lib', rally_conn_class_name, 'rally_vcs_connection') rescue Exception => ex problem = 'Unable to load RallyVCSConnection class, %s' % ex. boomex = VCSEIF_Exceptions::UnrecoverableException.new(problem) raise boomex, problem end @log.debug("Loading #{vcs_conn_class_name} class") begin @vcs_conn_class = ClassLoader.loadConnectionClass('lib', vcs_conn_class_name) rescue Exception => ex problem = 'Unable to load %sConnection class, %s' % [@vcs_name, ex.] boomex = VCSEIF_Exceptions::UnrecoverableException.new(problem) raise boomex, problem end @log.debug('Obtaining Rally and VCS connections...') establishConnections() @log.debug('Rally and VCS connections established') validate() # basically just calls validate on both connection instances @log.debug("loading Transform class ...") @transforms = loadTransforms(@txfm_conf) @log.debug("Transform loaded") @log.info("Initialization complete: Delegate connections operational, " +\ "aux facilities loaded, ready for scan/reflect ops") end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
34 35 36 |
# File 'lib/vcseif/vcs_connector.rb', line 34 def config @config end |
#log ⇒ Object (readonly)
Returns the value of attribute log.
34 35 36 |
# File 'lib/vcseif/vcs_connector.rb', line 34 def log @log end |
#rally_conf ⇒ Object (readonly)
Returns the value of attribute rally_conf.
37 38 39 |
# File 'lib/vcseif/vcs_connector.rb', line 37 def rally_conf @rally_conf end |
#rally_conn ⇒ Object (readonly)
Returns the value of attribute rally_conn.
35 36 37 |
# File 'lib/vcseif/vcs_connector.rb', line 35 def rally_conn @rally_conn end |
#rally_conn_class ⇒ Object (readonly)
Returns the value of attribute rally_conn_class.
35 36 37 |
# File 'lib/vcseif/vcs_connector.rb', line 35 def rally_conn_class @rally_conn_class end |
#svc_conf ⇒ Object (readonly)
Returns the value of attribute svc_conf.
37 38 39 |
# File 'lib/vcseif/vcs_connector.rb', line 37 def svc_conf @svc_conf end |
#transforms ⇒ Object (readonly)
Returns the value of attribute transforms.
38 39 40 |
# File 'lib/vcseif/vcs_connector.rb', line 38 def transforms @transforms end |
#txfm_conf ⇒ Object (readonly)
Returns the value of attribute txfm_conf.
37 38 39 |
# File 'lib/vcseif/vcs_connector.rb', line 37 def txfm_conf @txfm_conf end |
#vcs_conf ⇒ Object (readonly)
Returns the value of attribute vcs_conf.
37 38 39 |
# File 'lib/vcseif/vcs_connector.rb', line 37 def vcs_conf @vcs_conf end |
#vcs_conn ⇒ Object (readonly)
Returns the value of attribute vcs_conn.
35 36 37 |
# File 'lib/vcseif/vcs_connector.rb', line 35 def vcs_conn @vcs_conn end |
#vcs_conn_class ⇒ Object (readonly)
Returns the value of attribute vcs_conn_class.
35 36 37 |
# File 'lib/vcseif/vcs_connector.rb', line 35 def vcs_conn_class @vcs_conn_class end |
#vcs_name ⇒ Object (readonly)
Returns the value of attribute vcs_name.
36 37 38 |
# File 'lib/vcseif/vcs_connector.rb', line 36 def vcs_name @vcs_name end |
Instance Method Details
#collect_files_and_links(revnum, file_list, operation) ⇒ Object
349 350 351 352 353 354 355 356 357 |
# File 'lib/vcseif/vcs_connector.rb', line 349 def collect_files_and_links(revnum, file_list, operation) return_list = [] file_list.each do |rev_file| file_uri = @vcs_conn.get_file_rev_uri(revnum, rev_file, operation) log.debug(" Change file URI: #{file_uri}") return_list << {:file => rev_file, :link => file_uri} end return_list end |
#establishConnections ⇒ Object
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/vcseif/vcs_connector.rb', line 119 def establishConnections() @rally_conn = @rally_conn_class.new(@rally_conf, @log) @vcs_conn = @vcs_conn_class.new( @vcs_conf, @log) @vcs_conn.connect() # we do this before rally_conn to be able to get the vcs backend version vcs_backend_version = @vcs_conn.getBackendVersion() if @rally_conn != nil and @vcs_conn != nil and @rally_conn.respond_to?('set_integration_header') # so we can use it in our X-Rally-Integrations header items here rally_headers = {'name' => 'Rally VCSConnector for %s' % @vcs_name, 'version' => VERSION, 'vendor' => 'Rally Software', 'other_version' => vcs_backend_version } @rally_conn.set_integration_header(rally_headers) end @rally_conn.setSourceIdentification(@vcs_conn.name, @vcs_conn.uri) @rally_conn.connect() end |
#get_config_lookback(config) ⇒ Object
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
# File 'lib/vcseif/vcs_connector.rb', line 373 def get_config_lookback(config) default_value = 3600 # 1 hour in seconds lookback_val = config['Lookback'] return default_value if lookback_val.nil? || lookback_val.to_i <= 0 #default assumption is in minutes for lookback setting lookback_val = lookback_val.to_s.downcase.strip converted_val = lookback_val.to_i #rubyism: "5days".to_i = 5 "number".to_i = 0 return_val = (converted_val <= 0) ? default_value : converted_val * 60 #seconds per minute if lookback_val.include?("d") #5 days, 5days, 5d, 5 d return_val = (converted_val <= 0) ? default_value : converted_val * 86400 #seconds per day elsif lookback_val.include?("h") #24 hours, 24 hours, 24h, 24 h return_val = (converted_val <= 0) ? default_value : converted_val * 3600 #seconds per hour end return_val end |
#getRefTimes(last_commit_timestamp) ⇒ Object
359 360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/vcseif/vcs_connector.rb', line 359 def getRefTimes() """ last_commit_timestamp is provided as an epoch seconds value. Return a two-tuple of the reference time to be used for obtaining the recent changesets in Rally and the reference time to be used for obtaining the recent changesets in the target VCS system. """ rally_lookback = get_config_lookback(@rally_conf) vcs_lookback = get_config_lookback(@vcs_conf) rally_ref_time = Time.at( - rally_lookback).gmtime vcs_ref_time = Time.at( - vcs_lookback).gmtime return [rally_ref_time, vcs_ref_time] end |
#identifyUnrecordedChangesets(rally_changesets, vcs_changesets, ref_time) ⇒ Object
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 |
# File 'lib/vcseif/vcs_connector.rb', line 436 def identifyUnrecordedChangesets(rally_changesets, vcs_changesets, ref_time) """ Assume that for purposes of matching that the changeset identity in either system can be determined by the rev_ident (as opposed to the rev_num). If there are items in the rally_changesets for which there is a counterpart in the vcs_changesets, the information has already been reflected in Rally. --> NOOP If there are items in the rally_changesets for which there is no counterpart in the vcs_changesets, information has been lost, dat be berry berry bad... --> ERROR If there are items in the vcs_changesets for which there is no counterpart in the rally_changesets, those items are candidates to be reflected in Rally --> REFLECT """ rally_tank = {} # requirement: rc.ident <-- rc.Revision rally_changesets.each {|rally_chgset| rally_tank[rally_chgset.ident] = rally_chgset} vcs_tank = {} # requirement: vc.ident <-- {node} or {hash} or {number}, etc . ## ## puts "vcs_changesets ..." ## vcs_changesets.each { |cset| { puts cset.details } ## vcs_changesets.each {|vcs_chgset| vcs_tank[vcs_chgset.ident] = vcs_chgset} reflected_changesets = rally_changesets.select {|rcs| vcs_tank.has_key?(rcs.ident) == true } unpaired_changesets = rally_changesets.select {|rcs| vcs_tank.has_key?(rcs.ident) == false} unrecorded_changesets = vcs_changesets.select {|vcs| rally_tank.has_key?(vcs.ident) == false} if unpaired_changesets.length > 0 #lost_changesets = unpaired_changesets.select {|rcs| rcs.CommitTimestamp > ref_time} lost_changesets = [] unpaired_changesets.each do |rcs| rally_commit_time = Time.strptime(rcs.CommitTimestamp[0..18], ISO8601_TIMESTAMP) lost_changesets << rcs if rally_commit_time > ref_time end # Hold off on blurting this information in the log. It may just lead to confusion. # There _could_ be other systems throwing info into the Rally Change/Changeset pool # (although we wouldn't expect that to happen very often). #if not lost_changesets.empty? # problem = "Changesets exist in Rally for which there is no " # problem << "longer a counterpart changeset in the target VCS\n " # cs_idents = lost_changesets.collect {|cs| cs.ident} # @log.error(problem + cs_idents.join("\n ")) #end end return unrecorded_changesets end |
#internalizeConfig(config) ⇒ Object
104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/vcseif/vcs_connector.rb', line 104 def internalizeConfig(config) @rally_conf = config.topLevel('Rally') @vcs_conf = config.topLevel(@vcs_name) @svc_conf = config.topLevel('Services') @txfm_conf = config.topLevel('Transforms') || {} # default the Author transform to Passthru if not specified in @txfm_conf if not @txfm_conf.keys.include?('Author') @txfm_conf['Author'] = 'Passthru' end ## ## @log.debug("config.Services has |#{@svc_conf.inspect}|") ## end |
#postBatch(extension, status, repo_changesets) ⇒ Object
251 252 253 254 255 256 257 258 |
# File 'lib/vcseif/vcs_connector.rb', line 251 def postBatch(extension, status, repo_changesets) """ """ if extension != nil and extension =~ /PostBatchAction/ postba = extension['PostBatchAction'] postba.service(status, repo_changesets) end end |
#preBatch(extension) ⇒ Object
241 242 243 244 245 246 247 248 |
# File 'lib/vcseif/vcs_connector.rb', line 241 def preBatch(extension) """ """ if extension != nil and extension =~ /PreBatchAction/ preba = extension['PreBatchAction'] preba.service() end end |
#reflectChangesetsInRally(last_commit_timestamp) ⇒ Object
last_commit_timestamp is an instance of Time
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/vcseif/vcs_connector.rb', line 261 def reflectChangesetsInRally() """ The last commit timestamm is passed to Connection objects in UTC; they are responsible for converting if necessary. Time in log messages is always reported in UTC (aka Z or Zulu time). """ status = false rally = @rally_conn vcs = @vcs_conn rally_repo = @rally_conf['RepositoryName'] preview_mode = false preview_item = @svc_conf['Preview'].to_s.downcase preview_mode = true if ['true', 'yes', '1', 'ok', 'on'].include?(preview_item) pm_tag = '' action = 'adding' if preview_mode == true pm_tag = "Preview: " action = "would add" @log.info('***** Preview Mode ***** (no Changesets will be created in Rally)') end begin rally_ref_time, vcs_ref_time = getRefTimes() time_info = " ref times --> rally_ref_time: |%s| vcs_ref_time: |%s|" @log.debug(time_info % [rally_ref_time, vcs_ref_time]) recent_rally_changesets = rally.getRecentChangesets(rally_ref_time) @log.debug("Obtained Rally recent changesets info") recent_vcs_changesets = vcs.getRecentChangesets( vcs_ref_time) @log.debug("Obtained VCS recent changesets info") unrecorded_changesets = identifyUnrecordedChangesets(recent_rally_changesets, recent_vcs_changesets, vcs_ref_time) @log.info("%d unrecorded Changesets" % unrecorded_changesets.length) recorded_changesets = [] unrecorded_changesets.each do |changeset| cts = changeset..sub('Z', ' Z').sub('T', ' ') desc = '%sChangeset %16.16s | %s | %s not yet reflected in Rally' @log.debug(desc % [pm_tag, changeset.ident, cts, changeset.]) committer = changeset. transformEligibleAttributes(changeset) adds = collect_files_and_links(changeset.ident, changeset.adds, 'add') mods = collect_files_and_links(changeset.ident, changeset.mods, 'mod') dels = collect_files_and_links(changeset.ident, changeset.dels, 'del') info = { 'Revision' => changeset.ident, 'CommitTimestamp' => changeset., 'Committer' => committer, 'Author' => changeset., 'Message' => changeset., 'Additions' => adds, 'Modifications' => mods, 'Deletions' => dels, 'Uri' => @vcs_conn.get_rev_uri(changeset.ident) } desc = '%sChangeset %16.16s | %s | %s %s to Rally...' @log.info(desc % [pm_tag, changeset.ident, cts, changeset., action]) @log.debug(" Rev URI: #{info['Uri']}") if not preview_mode begin if not rally.changesetExists?(changeset.ident) rally_changeset = rally.createChangeset(info) recorded_changesets << rally_changeset end rescue Exception => ex raise VCSEIF_Exceptions::OperationalError.new(ex.) end end end status = true rescue VCSEIF_Exceptions::OperationalError => ex # already resulted in a log message rescue VCSEIF_Exceptions::UnrecoverableException => ex raise rescue Exception => ex raise end status = false if rally.operational_errors > 0 repo_changesets = {rally_repo => recorded_changesets} return [status, repo_changesets] end |
#run(last_commit_timestamp, extension) ⇒ Object
227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/vcseif/vcs_connector.rb', line 227 def run(, extension) """ Where the rubber meets the road, or rather, controlling the machinery that puts the rubber on the road... """ preBatch(extension) status, repo_changesets = reflectChangesetsInRally() postBatch(extension, status, repo_changesets) @rally_conn.disconnect() @vcs_conn.disconnect() return [status, repo_changesets] end |
#transformEligibleAttributes(changeset) ⇒ Object
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/vcseif/vcs_connector.rb', line 391 def transformEligibleAttributes(changeset) """ Check for a transformer for each transformable attribute, if no such transformer, leave the changeset.<attribute> value unchanged. Otherwise, get the transformer for the transformable attribute, call it's service method with: getattr(changeset, transformable_attribute.lower()) and use the result as the value for transformable_attribute, modifying the changeset item. """ @@transformable_attributes.each do |transformable_attribute| @log.debug("tranformable attribute: |#{transformable_attribute}|") if @transforms.include?(transformable_attribute) @log.debug("tranforms has a transformer class for #{transformable_attribute}") transformer = @transforms[transformable_attribute] target_attr = transformable_attribute.downcase() @log.debug("transformable target attribute: |#{target_attr}|") target_value = changeset.send(target_attr) @log.debug("transform target_value: |#{target_value}|") transformed = transformer.service(target_value) @log.debug("transformed to value: |#{transformed}|") if not transformed.nil? begin ## ## puts "#{changeset.class.name} attributes: #{changeset.instance_variables}" ## changeset.send("#{target_attr}=", transformed) rescue Exception => ex puts "#{ex.}" for bt_line in ex.backtrace do puts bt_line.sub(Dir.pwd, '${CWD}') end puts "#{ex.inspect}" end ## ## puts "Changeset.#{target_attr} new value: |#{changeset.send(target_attr)}| should be set to |#{transformed}| ?" ## else # TODO: should we log this? end end end end |
#validate ⇒ Object
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/vcseif/vcs_connector.rb', line 202 def validate() """ This calls the validate method on both the Rally and the VCS connections """ valid = true @log.info("Connector validation starting") if not @rally_conn.validate() @log.debug("RallyConnection validation failed") return false end @log.debug("RallyConnection validation succeeded") if not @vcs_conn.validate() @log.debug("%sConnection validation failed" % @vcs_name) return false end @log.debug("%sConnection validation succeeded" % @vcs_name) @log.info("Connector validation completed") return valid end |