public
Description: Perforce support for CruiseControl.rb
Homepage: http://cobaltedge.com/2008/06/perforce-support-for-cruise-co.html
Clone URL: git://github.com/chris/cruisecontrolrb_perforce.git
 
chris (author)
Wed Dec 17 23:43:55 -0800 2008
commit  b7e4ff2b9d44cad50c84e77486bb50f79c7c1dbd
tree    defb7fbb5113b6aeeb6119de6a97d4ff80331e3e
parent  91a792c243031352612e311d07d9253fe4e8e5d3
cruisecontrolrb_perforce / perforce.rb
100644 102 lines (80 sloc) 3.431 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
102
require 'builder_error'
 
# Perforce source control implementation for CruiseControl.rb
# Written by Christopher Bailey, mailto:chris@cobaltedge.com
class Perforce
  include CommandLine
 
  attr_accessor :port, :client_spec, :username, :password, :path
 
MAX_CHANGELISTS_TO_FETCH = 25
 
  def initialize(options = {})
    @port, @clientspec, @username, @password, @path, @interactive =
          options.delete(:port), options.delete(:clientspec),
options.delete(:user), options.delete(:password),
options.delete(:path), options.delete(:interactive)
    raise "don't know how to handle '#{options.keys.first}'" if options.length > 0
    @clientspec or raise 'P4 Clientspec not specified'
    @port or raise 'P4 Port not specified'
    @username or raise 'P4 username not specified'
    @password or raise 'P4 password not specified'
    @path or raise 'P4 depot path not specified'
  end
 
  def checkout(target_directory, revision = nil)
# No need for target_directory with Perforce, since this is controlled by
# the clientspec.
 
options = ""
    options << "#{@path}##{revision_number(revision)}" unless revision.nil?
 
    # need to read from command output, because otherwise tests break
    p4(:sync, options).each {|line| puts line.to_s }
  end
 
  def latest_revision(project)
# Get the latest changelist for this project
change = p4(:changes, "-m 1 #{@path}").first
 
# TODO: This isn't right - changesets I believe is supposed to be the set
# of files that were changed as part of this changelist.
changesets = [ ChangesetEntry.new(change['status'], change['desc']) ]
Revision.new(change['change'].to_i, change['user'], Time.at(change['time'].to_i), change['desc'], changesets)
  end
 
  def revisions_since(project, revision_number)
# This should get all changelists since the last one we used, but when using
# the -R flag with P4 it only seems to get the latest one.
changelists = p4(:changes, "-m #{MAX_CHANGELISTS_TO_FETCH} #{@path}@#{revision_number},#head")
 
changes = Array.new
changelists.each do |cl|
changeset = [ ChangesetEntry.new(cl['status'], cl['desc']) ]
changes << Revision.new(cl['change'].to_i, cl['user'], Time.at(cl['time'].to_i), cl['desc'], changeset)
end
    changes.delete_if { |r| r.number == revision_number }
 
changes
  end
 
  SYNC_PATTERN = /^(\/\/.+#\d+) - (\w+) .+$/
  def update(project, revision = nil)
sync_output = p4(:sync, revision.nil? ? "" : "#{@path}@#{revision_number(revision)}")
synced_files = Array.new
 
sync_output.each do |line|
      match = SYNC_PATTERN.match(line['data'])
      if match
        file, operation = match[1..2]
        synced_files << ChangesetEntry.new(operation, file)
      end
    end.compact
 
synced_files
  end
  
  private
  
# Execute a P4 command, and return an array of the resulting output lines
# The array will contain a hash for each line out output
  def p4(operation, options = nil)
p4cmd = "p4 -R -p #{@port} -c #{@clientspec} -u #{@username} -P #{@password} "
p4cmd << "#{operation.to_s}"
p4cmd << " " << options if options
 
p4_output = Array.new
IO.popen(p4cmd, "rb") do |file|
while not file.eof
p4_output << Marshal.load(file)
end
end
 
p4_output
  end
 
  def revision_number(revision)
    revision.respond_to?(:number) ? revision.number : revision.to_i
  end
  
  Info = Struct.new :revision, :last_changed_revision, :last_changed_author
end