#!/usr/bin/ruby
#--
#-------------------------------------------------------------------------------
#++
#== Introduction
#
# This script keeps a slave branch synchronised with a master branch based
# on the contents of a branch view. This could mean a single file, or an
# entire tree - it's up to you. There are two ways to limit the scope of
# the script:
#
# 1. Use a limited mapping in the trigger specification
# 2. Use a limited mapping in the branch specification
#
# As you can see, the methods are similar.
#
#== Requirements
#
# Ruby
# http://www.ruby-lang.org
#
# P4Ruby
# http://public.perforce.com/guest/tony_smith/perforce/API/Ruby/index.html
#
# Rubymail
# http://raa.ruby-lang.org/project/rubymail/
#
#== Trigger Specification
#
# An suitable trigger specification might look like this:
#
# autointeg commit //depot/master/... "t/autointeg.rb -b ms -c %changelist%"
#
# This would cause all changes to files under //depot/master/... that are
# mapped by the branch specification 'ms' (master-slave) to be propagated
# immediately to the slave copy.
#
#--
#-------------------------------------------------------------------------------
#++
require "P4"
require "getoptlong"
require "net/smtp"
require "rmail/message"
#--
#-------------------------------------------------------------------------------
# CONFIGURATION SECTION
#-------------------------------------------------------------------------------
#++
# Email address of the Perforce administrator - notifications will be
# sent to this user if EMAIL_REPORTS is true
ADMIN_EMAIL = "perforce@localhost"
# If true, reports of the integrations performed will be emailed to the
# address above. Default: false.
EMAIL_REPORTS = false
# The name of your SMTP server. The script uses SMTP to send notifications
# by email if EMAIL_REPORTS is true.
SMTP_SERVER = "localhost"
# The email address messages from this script should appear to come from
FROM_ADDRESS = "perforce@localhost"
# The subject line for the messages
MSG_SUBJECT = "Automatic integration report"
# Causes the text of the email that would be sent to be dumped to stderr -
# which in the case of a trigger means it goes to the client.
DEBUG = false
#
# Perforce environment. Unless these values are explicitly specified
# here, all the Perforce settings will be taken from the calling environment.
# This means you can use it with P4CONFIG files, and with 'p4 login'
# tickets if you want to. If you use tickets, make sure the script user is
# in a group with a really, really long ticket timeout.
P4USER = nil
P4PORT = nil
P4CLIENT = nil
P4PASSWD = nil
#--
#-------------------------------------------------------------------------------
# END OF CONFIGURATION SECTION
#-------------------------------------------------------------------------------
#++
#
# This simple class encapsulates the functionality of this script. Essentially,
# we integrate using a branchspec and auto resolve with 'Accept Theirs'. This
# means that there's no support for maintaining variant branches here, only
# master/slave branches. Useful for so-called 'shared files' (VSS-style).
#
class IntegMgr
def initialize( p4, branchspec )
@p4 = p4
@branch = branchspec
end
attr_reader :p4, :branch
#
# Integrate the specified change through the branch view and resolve
# them.
#
def integrate( change )
p4.run_sync
c = create_change( change )
p4.run_integrate( "-c", c, "-b", branch, "-s",
"//...@#{change},@#{change}" )
p4.run_resolve( "-at" )
# Build a list of the files to be submitted.
filelist = p4.run_opened( "-c", c ).collect do
|h|
sprintf( "%s#%s (%s)", h[ "depotFile" ], h[ "rev" ], h[ "action" ] )
end
# If no files were opened for integrate, there's nothing to do, so
# we just delete our pending changelist and get out of here
if filelist.length == 0
p4.delete_change( c )
return
end
# Now submit it.
p4.run_submit( "-c", c )
admin_report do
"Change #{change} automatically integrated using branchspec " +
@branch + "\n\n" +
"Affected Files:\n\n\t" +
filelist.join( "\n\t" ) +
"\n"
end
end
# Create a new empty changelist for this transaction
def create_change( changeno )
spec = p4.fetch_change
spec[ "Files" ] = Array.new
spec[ "Jobs" ] = Array.new
spec[ "Description" ] = "Automatically propagate change #{changeno} " +
"to slave branch."
change = p4.save_change( spec ).shift
if( change =~ /^Change (\d+)/ )
return $1
end
raise( P4Exception, "Unable to create an empty pending changelist" )
end
end
#
# Blurt out our syntax
#
def croaksyntax()
puts <<EOS
Usage: autointeg.rb -b <branchspec> -c <changelist>
EOS
exit 0
end
#
# Send a report by email to the admin
#
def admin_report( &block )
msg = RMail::Message.new
msg.header[ 'To' ] = ADMIN_EMAIL
msg.header[ 'From' ] = FROM_ADDRESS
msg.header[ 'Subject' ] = MSG_SUBJECT
msg.body = block.call()
if DEBUG
$stderr.puts( msg.to_s )
end
if EMAIL_REPORTS
Net::SMTP.start( SMTP_SERVER ) do
|smtp|
smtp.sendmail( msg, FROM_ADDRESS, ADMIN_EMAIL )
end
end
end
#--
#-------------------------------------------------------------------------------
# Start of main script
#-------------------------------------------------------------------------------
#++
opts = GetoptLong.new(
[ "-b", GetoptLong::REQUIRED_ARGUMENT ],
[ "-c", GetoptLong::REQUIRED_ARGUMENT ]
)
branch = nil
change = nil
# Parse command line
opts.each do
|opt,arg|
case opt
when "-b" then branch = arg
when "-c" then change = arg
end
end
croaksyntax unless ( branch && change )
#
# Command line is valid. Initialize our Perforce client and get to work.
#
p4 = P4.new
p4.user = P4USER if P4USER
p4.client = P4CLIENT if P4CLIENT
p4.port = P4PORT if P4PORT
p4.password = P4PASSWD if P4PASSWD
p4.parse_forms
p4.exception_level = 1
p4.debug = 0
begin
p4.connect
mgr = IntegMgr.new( p4, branch )
mgr.integrate( change )
rescue P4Exception
admin_report do
"Automatic integration failed with errors. Detail follows\n\n" +
"Error: #{$!.message}\n\n" +
"Perforce Errors:\n\t" +
p4.errors.join( "\n\t" ) +
"Traceback:\n\t" +
$!.backtrace.join( "\n\t" )
end
rescue
admin_report do
"Automatic integration failed with errors. Detail follows\n\n" +
"Error: #{$!.message}\n\n" +
"Traceback:\n\t" +
$!.backtrace.join( "\n\t" )
end
end
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #1 | 6014 | Robert Cowham | Bring in Tony's latest trigger changes | ||
| //guest/tony_smith/perforce/P4Rubylib/triggers/autointeg.rb | |||||
| #2 | 4765 | Tony Smith |
Bug fix. The script wasn't using the correct syntax for setting the mail headers. |
||
| #1 | 4640 | Tony Smith |
Add a sample post-commit trigger that can be used to keep a master and slave branch in sync. |
||