rscm-accurev 0.0.4 → 0.0.6

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.
data/Rakefile CHANGED
@@ -5,6 +5,7 @@ require 'rake/testtask'
5
5
  require 'rake/rdoctask'
6
6
  require 'rake/packagetask'
7
7
  require 'rake/gempackagetask'
8
+ require 'tracer'
8
9
 
9
10
  # this rakefile originally borrowed shamelessly from rscm
10
11
 
@@ -15,14 +16,24 @@ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
15
16
  PKG_SUMMARY = 'RSCM::Accurev - RSCM API for Accurev'
16
17
 
17
18
  desc "Default Task"
18
- task :default => [ :test, :gem ]
19
+ task :default => [ :test, :verify, :gem ]
19
20
 
20
- #task :test => [ :backup_testresults, :xmltest ]
21
+ RAKEVERSION =~ /^(\d+)\.(\d+).(.*)/
22
+ rake_maj,rake_min,rake_patch = $1,$2,$3
23
+ if rake_maj.to_f < 1 and rake_min.to_f <= 5 and rake_patch.to_f <= 4
24
+ # use this for rake <= 0.5.4:
25
+ task :test => [ :plaintest ]
26
+ HAVEXMLTEST = false
27
+ else
28
+ # rake 0.5.4.6+ (0.5.5)
29
+ task :test => [ :backup_testresults, :xmltest ]
30
+ HAVEXMLTEST = true
31
+ end
21
32
 
22
33
  # define a task(s) to run tests
23
34
 
24
35
  test_files = FileList.new( "test/**/t_*.rb" )
25
- TEST_OUTPUT = "doc"
36
+ TEST_OUTPUT = "html/test"
26
37
 
27
38
  Rake::TestTask.new( :plaintest ) do |t|
28
39
  t.libs << "test"
@@ -30,8 +41,9 @@ Rake::TestTask.new( :plaintest ) do |t|
30
41
  t.verbose = true
31
42
  end
32
43
 
33
- # requires a patch to rake_test_loader.rb to exclude options
34
- # requires a patch to rake/testtask.rb to support ruby_opts
44
+ if HAVEXMLTEST
45
+ # rake<0.5.5 requires a patch to rake_test_loader.rb to exclude options
46
+ # rake<0.5.5 requires a patch to rake/testtask.rb to support ruby_opts
35
47
  # (see doc/patches)
36
48
  Rake::TestTask.new( :xmltest ) do |t|
37
49
  t.libs << "test"
@@ -41,9 +53,14 @@ Rake::TestTask.new( :xmltest ) do |t|
41
53
  ENV['XMLTEST_OUTPUT'] = "#{TEST_OUTPUT}/testresults.xml"
42
54
  t.options = "--runner=xml"
43
55
  end
56
+ end # !HAVEXMLTEST
57
+
44
58
 
45
59
  # backs up existing testresults.xml files in test target dir
46
60
  task :backup_testresults do
61
+ unless File.exist?( TEST_OUTPUT )
62
+ mkdir_p( TEST_OUTPUT )
63
+ end
47
64
  old = "#{TEST_OUTPUT}/testresults.xml"
48
65
  if File.exist?( old )
49
66
  stat = File.stat( old )
@@ -97,9 +114,39 @@ else
97
114
  end
98
115
 
99
116
  desc "Delete all generated files (includes built packages)"
100
- task :realclean => :clean do
117
+ task :realclean do
101
118
  f = FileList[ 'pkg', 'html', 'doc/test*.xml' ]
102
- rm f
119
+ rm_rf f
103
120
  end
104
121
 
105
-
122
+ desc "Coverage! (still a big hack)"
123
+ task :coverage => [ :test_coverage, :coverage_summary ]
124
+
125
+ Rake::TestTask.new( :test_coverage ) do |t|
126
+ t.libs << "test"
127
+ t.test_files = test_files
128
+ t.verbose = true
129
+ def t.rake_loader
130
+ 'test/coverage/c_loader.rb'
131
+ end
132
+ end
133
+
134
+ task :coverage_summary do
135
+ # too many options!@
136
+ ruby "test/coverage/analyzer.rb xxx.trace html/coverage test/coverage/template.html test/coverage/index_tmpl.html"
137
+ end
138
+
139
+ desc "Perform various prerelease verifications"
140
+ task :verify do
141
+ require 'find'
142
+ Find.find(".") do |path|
143
+ if FileTest.directory?(path)
144
+ next
145
+ end
146
+ next if path =~ /^.\/tmp\/cleaner.pl$/
147
+ next if path =~ /^.\/Rakefile$/
148
+ if File.read(path) =~ /orbitz|duncllc|gfast|fduncan/i
149
+ fail "Cannot release #{path}, please clean up"
150
+ end
151
+ end
152
+ end
data/TODO CHANGED
@@ -37,5 +37,5 @@ api - update: elements list should probably not include removed files
37
37
 
38
38
  *** api - checkout() should use `pop` instead of `mkws`, unless
39
39
  user *really* wants it
40
-
40
+ - yeargh... how does update work with pop? just nuke and/or overwrite?
41
41
 
@@ -5,6 +5,9 @@
5
5
  LAST=`cat .version.minor`
6
6
  MINOR=$(( $LAST + 1 ))
7
7
 
8
+ perl -i~~ -pe "s/VERSION = '0.0.\d+'/VERSION = '0.0.$MINOR'/" \
9
+ lib/rscm/accurev.rb
10
+
8
11
  PKG_BUILD=$MINOR rake package gem && \
9
12
  echo $MINOR > .version.minor
10
13
 
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems'
3
2
  require 'rscm/scm/accurev/api'
4
3
  require 'rscm/scm/accurev/command'
5
4
  require 'rscm/scm/accurev/xml'
@@ -13,5 +12,7 @@ module RSCM
13
12
  #
14
13
  # See RSCM::Accurev::API.
15
14
  #
16
- module Accurev; end
15
+ module Accurev
16
+ VERSION = '0.0.6'
17
+ end
17
18
  end
@@ -4,41 +4,24 @@
4
4
  #
5
5
  # API for interacting with accurev workspaces.
6
6
  #
7
+ # Includes:
8
+ #
9
+ # * API
10
+ #
7
11
  require 'stringio'
8
- require 'rubygems'
9
12
  require 'rscm/base'
10
13
  require 'rscm/path_converter'
11
14
  require 'rexml/document'
12
15
  require 'rscm/scm/accurev/xml'
13
16
  require 'rscm/scm/accurev/filterio'
14
17
  require 'rscm/scm/accurev/command'
18
+ require 'rscm/scm/accurev/exception'
15
19
 
16
20
  # need to define module within module before you can use module X::Y
17
21
  module RSCM; module Accurev; end; end
18
22
 
19
23
  module RSCM::Accurev
20
24
 
21
- #
22
- # General exception class for errors processing commands.
23
- #
24
- # @attr error_msg Error message output by accurev.
25
- #
26
- class AccurevException < Exception
27
- attr_reader :error_msg
28
- def initialize( msg, error_msg=nil )
29
- super( "#{msg}: #{error_msg}" )
30
- @error_msg = error_msg
31
- end
32
- end
33
-
34
- #
35
- # Exception thrown when an update is performed
36
- # on a workspace with unkept/unanchored modifications.
37
- # Accurev requires that all modifications be handled before
38
- # an update is performed.
39
- #
40
- class StaleWorkspaceException < AccurevException; end
41
-
42
25
  #
43
26
  # RSCM implementation for Accurev (http://www.accurev.com).
44
27
  #
@@ -64,30 +47,25 @@ module RSCM::Accurev
64
47
  ann :description => "Backing Stream (autodetected)"
65
48
  attr_accessor :backing_stream
66
49
 
50
+ # If workspace stream is nil (the default), checkout/update will be
51
+ # done using `accurev pop`. This is faster and more appropriate for
52
+ # read-only use; however a workspace is required for commits.
67
53
  ann :description => "Workspace Stream"
68
54
  attr_accessor :workspace_stream
69
55
 
70
56
  # [defined in Base]
71
57
  # ann :description => "Filesystem Path to Workspace"
72
58
  # attr_accessor :checkout_dir
59
+
60
+ ann :description => "Overwrite Mode: if true, co overwrites existing"
61
+ attr_accessor :overwrite
73
62
 
74
- # status flag mappings for "stat", "file" commands
75
- STATUSES = {
76
- '(backed)' => :backed,
77
- '(external)' => :external,
78
- '(modified)' => :modified,
79
- '(kept)' => :kept,
80
- '(defunct)' => :defunct,
81
- '(missing)' => :missing,
82
- '(stranded)' => :stranded,
83
- '(overlap)' => :overlap
84
- }
85
-
86
- def initialize( checkout_dir=nil, depot=nil, workspace_stream=nil )
87
- @depot = depot
63
+ def initialize(checkout_dir=nil, backing_stream=nil, workspace_stream=nil)
64
+ @depot = nil # will be pulled from files cmd output
88
65
  @workspace_stream = workspace_stream
89
- @backing_stream = nil # will be pulled from files cmd output
66
+ @backing_stream = backing_stream
90
67
  @checkout_dir = checkout_dir
68
+ @overwrite = false
91
69
  end
92
70
 
93
71
  def name()
@@ -109,8 +87,30 @@ module RSCM::Accurev
109
87
  end
110
88
 
111
89
  # "Returns a Revisions object for the period specified ... (inclusive)."
90
+ #
91
+ # list txns (promotes to bs) from hist within the period
92
+ #
112
93
  def revisions( from_identifier, to_identifier=Time.infinity )
113
- raise "XXX implement me"
94
+ revisions = RSCM::Revisions.new()
95
+ self.ac_hist( from_identifier, to_identifier ).each do |txn|
96
+ txnrev = RSCM::Revision.new
97
+ txnrev.identifier = txn.id
98
+ txnrev.developer = txn.user
99
+ txnrev.message = txn['comment'].join("\n") unless txn['comment'].nil?
100
+ txnrev.time = Time.at( txn.time.to_i )
101
+ txn['version'].each do |v|
102
+ f = RSCM::RevisionFile.new()
103
+ f.path = v.path
104
+ # i think to get real status, you'd need to analyze keep txns
105
+ f.status = RSCM::RevisionFile::MODIFIED # XXX? txn.type==promote here
106
+ f.developer = txn.user
107
+ f.native_revision_identifier = v.real # XXX or .virtual?
108
+ f.time = Time.at( txn.time.to_i )
109
+ txnrev << f
110
+ end
111
+ revisions.add( txnrev )
112
+ end
113
+ return revisions
114
114
  end
115
115
 
116
116
  # default impl depends on a correct impl of revisions()
@@ -130,11 +130,29 @@ module RSCM::Accurev
130
130
 
131
131
  # --- ac specific
132
132
 
133
+ # remember: ac_files is non-recursive
133
134
  def ac_files( relative_path )
134
135
  cmd = Command.instance
135
136
  ret = []
136
- with_working_dir( @checkout_dir ) do
137
+ with_working_dir( co_filepath ) do
137
138
  acresponse = cmd.accurev( "files", relative_path )
139
+ if acresponse['element'].nil?
140
+ return []
141
+ end
142
+ acresponse['element'].each do |fd|
143
+ yield fd if block_given?
144
+ ret << fd
145
+ end
146
+ end
147
+ return ret
148
+ end
149
+
150
+ def ac_stat( flag="-a" )
151
+ cmd = Command.instance
152
+ ret = []
153
+ with_working_dir( co_filepath ) do
154
+ acresponse = cmd.accurev( "stat", flag )
155
+ return [] if acresponse['element'].nil?
138
156
  acresponse['element'].each do |fd|
139
157
  yield fd if block_given?
140
158
  ret << fd
@@ -143,6 +161,29 @@ module RSCM::Accurev
143
161
  return ret
144
162
  end
145
163
 
164
+ # yeilds the TransactionData objects for the given time period
165
+ # (promotes only)
166
+ def ac_hist( from, to=Time.infinite )
167
+ cmd = Command.instance
168
+ lower = from.to_accurev
169
+ upper = "now"
170
+ unless to == Time.infinite
171
+ upper = to.to_accurev
172
+ end
173
+ with_working_dir( self.co_filepath ) do
174
+ acresponse = cmd.accurev( "hist",
175
+ "-t", "'#{upper}-#{lower}'",
176
+ "-k", "promote"
177
+ )
178
+ ret = []
179
+ acresponse['transaction'].each do |txn|
180
+ ret << txn
181
+ yield txn if block_given?
182
+ end
183
+ return ret
184
+ end
185
+ end
186
+
146
187
  def ac_keep( files=[], message="" )
147
188
  raise "XXX implement me"
148
189
  end
@@ -180,7 +221,7 @@ module RSCM::Accurev
180
221
  def ac_info()
181
222
  co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
182
223
  unless File.exists?( co )
183
- raise AccurevException.new( "Checkout dir does not exist" )
224
+ raise AccurevException.new( "Checkout dir #{co} does not exist" )
184
225
  end
185
226
  info = {}
186
227
  with_working_dir( co ) do
@@ -196,19 +237,6 @@ module RSCM::Accurev
196
237
  return info
197
238
  end
198
239
 
199
- # ac mkws
200
- # -b orbitz-host-gt3-0-impl
201
- # -w orbitz-host-gt3-0-impl-test2_gfast
202
- # -l `pwd`
203
- # - does not support fx (argh!@)
204
- # - does not pop
205
- #
206
-
207
- # diff: /usr/local/bin/acdiff (!) --fx
208
- # (after an ac cat on the backed file)
209
- # (eg, no in-process diffing ala cvs diff)
210
- # lists modified lines in xml
211
-
212
240
  #
213
241
  # "Checks out or updates[!] contents from a central SCM
214
242
  # to +checkout_dir+ - a local working copy."
@@ -232,28 +260,68 @@ module RSCM::Accurev
232
260
  #
233
261
  def checkout( to_identifier=Time.infinite )
234
262
  co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
235
- unless File.exists?( co )
236
- puts "> creating new workspace..."
237
- if @backing_stream.nil?
238
- raise "Backing stream must be set"
263
+ if @backing_stream.nil?
264
+ self.attempt_init_from_info()
265
+ end
266
+ if @workspace_stream.nil?
267
+ self.checkout_pop()
268
+ else
269
+ unless File.exists?( co )
270
+ # create new workspace
271
+ self.checkout_workspace()
239
272
  end
240
- if @workspace_stream.nil?
241
- raise "Workspace stream must be set"
273
+ # update workspace
274
+ self.update( to_identifier )
275
+ end
276
+ end
277
+
278
+ def checkout_pop()
279
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
280
+ raise "A backing stream is required" if @backing_stream.nil?
281
+ raise "A working stream may not be given" unless @working_stream.nil?
282
+ # for `accurev pop`: remove and re-pop the checkout copy
283
+ if File.exists?( co )
284
+ unless @overwrite
285
+ raise "Checkout dir #{co} already exists (@overwrite=#@overwrite)"
242
286
  end
243
- Command.instance do |cmd|
244
- cmd.working_dir = co
245
- mkws_out = cmd.accurev_nofx( "mkws",
246
- "-b", @backing_stream,
247
- "-w", @workspace_stream,
248
- "-l", co )
249
- # sucks:
250
- if ( mkws_out =~ /already exists/ )
251
- raise AccurevException.new( "Failed to checkout", mkws_out )
287
+ rm_rf( co )
288
+ end
289
+ mkdir(co)
290
+ with_working_dir( co ) do
291
+ cmd = Command.instance
292
+ pop_out = cmd.accurev_nofx("pop",
293
+ "-R",
294
+ "-v", @backing_stream,
295
+ "-L", ".", ".")
296
+ elems = []
297
+ popio = StringIO.new( pop_out )
298
+ popio.each_line do |line|
299
+ if line =~ /^Populating element \/.\/(.*)$/
300
+ loc = $1
301
+ elems << loc
302
+ yield loc if block_given?
252
303
  end
253
304
  end
305
+ return elems
306
+ end
307
+ end
308
+
309
+ def checkout_workspace()
310
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
311
+ raise "A backing stream is required" if @backing_stream.nil?
312
+ raise "A workspace is required" if @working_stream.nil?
313
+ if File.exist?( co ) and !@overwrite
314
+ raise "Checkout dir #{co} already exists (@overwrite=#@overwrite)"
315
+ end
316
+ cmd = Command.instance
317
+ mkws_out = cmd.accurev_nofx( "mkws",
318
+ "-b", @backing_stream,
319
+ "-w", @workspace_stream,
320
+ "-l", co )
321
+ # kinda sucks:
322
+ if ( mkws_out =~ /already exists/ )
323
+ raise AccurevException.new( "Failed to checkout", mkws_out )
254
324
  end
255
- puts "> Updating workspace.."
256
- self.update( to_identifier )
257
325
  end
258
326
 
259
327
  def update( to_identifier=Time.infinite )
@@ -290,7 +358,43 @@ module RSCM::Accurev
290
358
 
291
359
  # --- internals
292
360
 
293
- private
361
+ # status flag mappings for "stat", "file" commands
362
+ STATUSES = {
363
+ '(backed)' => :backed,
364
+ '(external)' => :external,
365
+ '(modified)' => :modified,
366
+ '(kept)' => :kept,
367
+ '(defunct)' => :defunct,
368
+ '(missing)' => :missing,
369
+ '(stranded)' => :stranded,
370
+ '(overlap)' => :overlap
371
+ }
372
+
373
+ # private
374
+
375
+ # psuedo-accessor (cached)
376
+ def co_filepath
377
+ if @co_filepath.nil?
378
+ @co_filepath = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
379
+ end
380
+ return @co_filepath
381
+ end
382
+
383
+ #
384
+ # Runs an `ac info` command in +@checkout_dir+, and tries to
385
+ # set +@backing_stream+ and +@workspace_stream+ from the output.
386
+ #
387
+ def attempt_init_from_info()
388
+ if File.exists?( self.co_filepath )
389
+ info = self.ac_info
390
+ if info.has_key?( "Basis" )
391
+ @backing_stream = info["Basis"]
392
+ end
393
+ if info.has_key?( "ws/ref" )
394
+ @workspace_stream = info["ws/ref"]
395
+ end
396
+ end
397
+ end
294
398
 
295
399
  # Takes a status flags line (eg, "(modified)(kept)")
296
400
  # and returns a list of status flags.
@@ -302,6 +406,12 @@ module RSCM::Accurev
302
406
 
303
407
  end
304
408
 
409
+ class Time
410
+ def to_accurev
411
+ self.strftime( "%Y/%m/%d %H:%M:%S" )
412
+ end
413
+ end
414
+
305
415
  #
306
416
  # Copyright (c) 2005 Gregory D. Fast <gdf@speakeasy.net>
307
417
  #