rscm-accurev 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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
  #