#!/usr/bin/ruby #-- #------------------------------------------------------------------------------- #++ # #= Introduction # #== Name: default_client.rb # #== Author: Robert Cowham # Tony Smith # #== Description # # Example trigger to set some default client options # #== Requires # Ruby # P4Ruby # P4Triggers module # #== Example 'triggers' section: # # Triggers: # default_client form-out client "ruby c:/perforce/scripts/default_client.rb -p %serverport% -u perforce template_client %formname% %formfile% " # # would make the default client view offered to any user based on # the view defined in the client called 'template_client'. Changing that # client spec would change the default view for future clients. # #== Note # # Either use a P4CONFIG file or specify -p etc using TriggerOptions # #-- #------------------------------------------------------------------------------- #++ $:.unshift( File.dirname( __FILE__ ) ) require "P4" require "P4Triggers" require "ostruct" class ClientTrigger < P4Trigger def initialize( options, client, template, filename ) super( options ) @client = client @template = template @formfile = P4Trigger::FormFile.new( filename ) p4.exception_level = 1 p4.connect end # # Check whether or not a client already exists. We do this using 'p4 info' # rather than 'p4 clients' since running 'p4 clients' on a large system # can be very expensive. # def client_exists?( name ) tp4 = P4.new tp4.client = name tp4.port = p4.port? tp4.user = p4.user? tp4.tagged tp4.connect info = tp4.run_info.shift tp4.disconnect return true if info.has_key?( 'clientRoot' ) return false end # # Main method - loads the template spec, and makes the alterations to # the formfile in place. # def update_client() begin # # Sanity checks: # # (a) Don't do anything if the client already exists # (b) Don't do anything if the template doesn't exist # (c) Don't do anything if the client in question IS the template! # # Note that (c) is VITAL to prevent infinite recursion caused by # an 'out' trigger running a 'p4 client -o'!!! # return 0 if( client_exists?( @client ) ) return 0 unless( client_exists?( @template ) ) return 0 if( @client == @template ) # First fetch the template spec. This will cause our Perforce # server to execute this script again in another thread/process. # Fortunately, check (c) above stops the infinite loop # # Note that this also gets the specdef for clients from this server # so that P4Ruby can cache it so parse_client() and format_client() # will not send any commands to the server. tspec = p4.fetch_client( @template ) cspec = p4.parse_client( @formfile.load() ) # Make sure that the clientspec is the default one which contains 1 or more mappings of depots only, e.g. # //depot1/... //cs/depot1/... # This handles the case where the user is copying a client from an existing template which we allow (by aborting) default = true cspec[ "View" ].each do |mapping| if mapping !~ %r(^//[^/]+/\.\.\.\s+) then default = false end end return 0 if ( !default ) # Now copy across the options and view from the template clientspec. # For the view, we have to replace the template name with the client # name as we copy. cspec[ "View" ] = tspec[ "View" ].collect do |mapping| mapping.sub( "//#{@template}/", "//#{@client}/" ) end cspec[ "Options" ] = tspec[ "Options" ] cspec[ "SubmitOptions" ] = tspec[ "SubmitOptions" ] cspec[ "Description" ] = tspec[ "Description" ] # If root is on a windows drive (something:) then set new default cspec[ "Root" ].sub!(/\s*([^\n]*)/) { match = $1.dup case match when /^\w:/ result = "d:\\work\\" + @client else result = match end result } # Now update the existing formfile with the new spec. @formfile.save( p4.format_client( cspec ) ) return 0 rescue return report_error() end end end #-- #------------------------------------------------------------------------------- # LOCAL FUNCTIONS #------------------------------------------------------------------------------- #++ # # Show a message explaining correct usage of this script and exit. # def croakusage() puts < Where: - the name of an existing client workspace on which all future client workspaces should be based (by default). - The name of the client spec the user is asking for - The name of the temporary file in which the current client spec is stored EOS exit( 1 ) end #-- #------------------------------------------------------------------------------- # START OF MAIN SCRIPT #------------------------------------------------------------------------------- #++ def main # parse options options = P4TriggerOptions.new(ARGV) options.parse_options!() # Check remaining parameters croakusage() unless ARGV.length() == 3 template = ARGV.shift; client = ARGV.shift; formfile = ARGV.shift; # # This next line is CRITICAL. It prevents an infinite recursion which will # only terminate when your server runs out of resources. This is because # this script is triggered whenever a user runs a 'p4 client -o' and this # script itself runs a 'p4 client -o' ... # exit( 0 ) if( client == template) trig = ClientTrigger.new( options, client, template, formfile ); exit( trig.update_client() ) end if __FILE__ == $0 main end