# # File:: bootstrap.rb # Description:: Bootstrap # - This script will analyse the return codes passed and perform bootstrap functionality. # - See RSG.Pipeline.Core.Constants # - Resume ( facilitated by return code 1 ) # - Sync tools head # - Sync tools labelled # - Sync tools config # - Tools install ( coming soon?! ) # Author:: Derek Ward # Date:: 18 March 2013 # #----------------------------------------------------------------------------- # Uses #----------------------------------------------------------------------------- require 'RSG.Base.dll' require 'RSG.Pipeline.Core.dll' require 'mscorlib' require 'System.Core' require 'RSG.Base.Configuration.dll' require 'RSG.Base.Windows.dll' require 'RSG.SourceControl.Perforce.dll' require 'RSG.Pipeline.Automation.Common.dll' require 'RSG.Pipeline.Services.dll' include RSG::Base::Configuration include RSG::Base::Logging include RSG::Base::Logging::Universal include RSG::Base::OS include RSG::Base::Windows include RSG::Pipeline::Core include RSG::SourceControl::Perforce include RSG::Pipeline::Automation::Common include RSG::Pipeline::Services require 'pipeline/os/options' include Pipeline require 'fileutils' #----------------------------------------------------------------------------- # Constants #----------------------------------------------------------------------------- AUTHOR = 'RSGEDI Tools' EMAIL = 'RSGEDI Tools <*tools@rockstarnorth.com>' OPTIONS = [ LongOption::new( 'host', LongOption::ArgType.Optional, 'h', 'host' ), LongOption::new( 'role', LongOption::ArgType.Optional, 'r', 'role' ), LongOption::new( 'automation', LongOption::ArgType.Optional, 'a', 'automation' ), LongOption::new( 'filetransfer', LongOption::ArgType.Optional, 'f', 'filetransfer' ), ] SYNC_SLEEP = 1 # Dont muck around unless you know what your doing SYNC_SLEEP_LAST = 10 # Dont muck around unless you know what your doing #----------------------------------------------------------------------------- # Functions #----------------------------------------------------------------------------- # # Kill the automation console # wait for other RSG.Pipeline.automation processes to terminate # otherwise a sync to an automation dll/exe will find itself in use. def KillConsoleAndWaitForProcesses(log) log.Message(" ") log.Message("*********************************************************************") log.Message("*** Bootstrap: Taskkill local RSG.Pipeline.Automation.Console.exe ***") log.Message("*********************************************************************") log.Message(" ") kill_console_cmd = 'taskkill /F /IM rsg.pipeline.automation.console.exe 1>NUL 2>NUL' system(kill_console_cmd) log.Message(" ") log.Message("*************************************************************************") log.Message("*** Bootstrap: Wait for processes RSG.Pipeline.Automation.* to exit ***") log.Message("*************************************************************************") log.Message(" ") # Now wait until processes with a particular naming convention have terminated begin found_process = false processes = System::Diagnostics::Process.GetProcesses() processes.each do |process| if process.ProcessName =~ /(RSG\.Pipeline\.Automation.*)/i process_name = $1 if (not process.ProcessName =~ /.*vshost.*/i) log.Message("bootstrap.rb is waiting on process : " + process_name) found_process = true end end end # ALWAYS sleep, even if there are no processes. Kernel.sleep(SYNC_SLEEP) end while found_process == true log.Message(" ") log.Message("**********************************************************************") log.Message("*** Bootstrap: All processes RSG.Pipeline.Automation.* have exited ***") log.Message("**********************************************************************") log.Message(" ") end #----------------------------------------------------------------------------- # Entry-Point #----------------------------------------------------------------------------- if ( __FILE__ == $0 ) then g_Options = OS::Options::new( OPTIONS ) begin if ( g_Options.is_enabled?( 'help' ) ) puts "#{__FILE__}" puts "Usage:" puts g_Options.usage() exit( 1 ) end exit( 0 ) if ( 0 == ARGV.size ) LogFactory.Initialize() LogFactory.CreateApplicationConsoleLogTarget( ) g_Log = LogFactory.ApplicationLog g_Config = RSG::Base::Configuration::ConfigFactory::CreateConfig( ) g_Log.Message(" ") g_Log.Message("*****************") g_Log.Message("*** Bootstrap ***") g_Log.Message("*****************") g_Log.Message(" ") bootstrap_retcode = 0 # Examine return codes passed in to process - act on each g_Options.trailing.each do |bootstrap_code| g_Log.Message("Processing bootstrap code #{bootstrap_code}") boot_code = Integer(bootstrap_code) next if (boot_code==nil) # ignore all crap toolsFileSpec = "#{g_Config.ToolsRoot}/..." p4_sync_cmds = [] p4_sync_path = System::IO::Path::Combine(ENV['RS_TOOLSROOT'], 'tmp', ENV['RS_BOOTSTRAP_NAME']) FileUtils.mkdir_p p4_sync_path p4_sync_filename = System::IO::Path::Combine(p4_sync_path, 'p4_automation_tools_sync.bat') # # CLEAN CACHE if (boot_code & Constants.Exit_CleanCache > 0) KillConsoleAndWaitForProcesses(g_Log) # Build cache folder path cache_folder = System::IO::Path::Combine(ENV['RS_PROJROOT'], 'cache', 'automation'); g_Log.Message(" ") g_Log.Message("*****************************************************") g_Log.Message("*** Bootstrap: Cleaning cache {0} ***", cache_folder) g_Log.Message("*****************************************************") g_Log.Message(" ") # Get folder and delete it RSG::Pipeline::Services::Cache::ClearDirectory(g_Log, cache_folder) g_Log.Message(" ") g_Log.Message("*****************************************************") g_Log.Message("*** Bootstrap: Cache cleaned ***") g_Log.Message("*****************************************************") g_Log.Message(" ") end # # TOOLS SYNC HEAD if (boot_code & Constants.Exit_SyncHead > 0) KillConsoleAndWaitForProcesses(g_Log) g_Log.Message(" ") g_Log.Message("*****************************************************") g_Log.Message("*** Bootstrap: Sync to tools head will occur soon ***") g_Log.Message("*****************************************************") g_Log.Message(" ") # # DW: No idea why - despite all attempts you can't run p4 sync using ironlib assemblies # the solution is to run the sync once this script exits # p4_sync_cmds << "p4 sync #{toolsFileSpec}" g_Log.Message(" ") g_Log.Message("********************************") g_Log.Message("*** Bootstrap: Sleep for {0}s ***", SYNC_SLEEP_LAST) g_Log.Message("********************************") g_Log.Message(" ") # This sleep is important - it will reduce the likelihood that a process will restart when another process is still syncing. # the biggest danger with this is p4 latency, but otherwise is quite unlikely to fail. Kernel.sleep(SYNC_SLEEP_LAST) end # # TOOLS SYNC LABEL if (boot_code & Constants.Exit_SyncLabel > 0) KillConsoleAndWaitForProcesses(g_Log) g_Log.Message(" ") g_Log.Message("*********************************************************") g_Log.Message("*** Bootstrap: Sync to labelled tools will occur soon ***") g_Log.Message("*********************************************************") g_Log.Message(" ") label = g_Config.Project.Labels[Label.ToolsCurrent] toolsFileSpecLabel = "#{toolsFileSpec}@#{label},@#{label}" p4_sync_cmds << "p4 sync #{toolsFileSpecLabel}" g_Log.Message(" ") g_Log.Message("************************************") g_Log.Message("*** Bootstrap: Now sleep for {0}s ***", SYNC_SLEEP_LAST) g_Log.Message("************************************") g_Log.Message(" ") # This sleep is important - it will reduce the likelihood that a process will restart when another process is still syncing. # the biggest danger with this is p4 latency, but otherwise is quite unlikely to fail. Kernel.sleep(SYNC_SLEEP_LAST) end # # TOOLS SYNC CONFIG sync_config = boot_code & Constants.Exit_SyncConfig > 0 sync_content = boot_code & Constants.Exit_SyncContent > 0 if (sync_config or sync_content) KillConsoleAndWaitForProcesses(g_Log) g_Log.Message(" ") g_Log.Message("********************************************************") g_Log.Message("*** Bootstrap: Sync to tools config will occur soon ***") if (sync_config) g_Log.Message("*** Bootstrap: Sync to tools content will occur soon ***") if (sync_content) g_Log.Message("********************************************************") g_Log.Message(" ") p4 = RSG::SourceControl::Perforce::P4.new() all_files = [] if (sync_config) g_Log.Message(" ") g_Log.Message("\tCONFIG FILES") g_Log.Message(" ") files = ConfigSync.DepotFilesTriggeringRestartResync(p4, g_Options.command_options.Branch, g_Config, sync_config, false) files.each do |file| all_files << file g_Log.Message("\t#{file}") end end if (sync_content) g_Log.Message(" ") g_Log.Message("\tCONTENT FILES") g_Log.Message(" ") files = ConfigSync.DepotFilesTriggeringRestartResync(p4, g_Options.command_options.Branch, g_Config, false, sync_content) files.each do |file| all_files << file g_Log.Message("\t#{file}") end end # splice it up into managebale chunks due to B* 1839527 all_files_chunks = all_files.each_slice(100).to_a all_files_chunks.each do |chunk_files| files_joined = chunk_files.join(" ") p4_sync_cmds << "p4 sync #{files_joined}" end g_Log.Message(" ") g_Log.Message("************************************") g_Log.Message("*** Bootstrap: Now sleep for {0}s ***", SYNC_SLEEP_LAST) g_Log.Message("************************************") g_Log.Message(" ") # This sleep is important - it will reduce the likelihood that a process will restart when another process is still syncing. # the biggest danger with this is p4 latency, but otherwise is quite unlikely to fail. Kernel.sleep(SYNC_SLEEP_LAST) end # # Write the p4 sync command to tools tmp - so we can run it later ( outside of ironruby - cos it keeps dlls open ) File.open(p4_sync_filename, 'w') do |f| f.write("ECHO.\n") f.write("ECHO *** BOOTSTRAP SYNCING NOW ***\n") p4_sync_cmds.each do |p4_sync_cmd| if (p4_sync_cmd != '') f.write("ECHO *** #{p4_sync_cmd}\n") f.write("ECHO.\n") end f.write("\n") f.write(p4_sync_cmd) f.write("\n") end if (p4_sync_cmds.length >= 0) f.write("ECHO *** BOOTSTRAP SYNCING COMPLETED ***\n") end end # # RESUME if (boot_code & Constants.Exit_Resume > 0) bootstrap_retcode = 1 # This is the signal to the calling script to resume g_Log.Message(" ") g_Log.Message("*************************") g_Log.Message("*** Bootstrap: Resume ***") g_Log.Message("*************************") g_Log.Message(" ") end end g_Log.Message("Bootstrap exiting with #{bootstrap_retcode}") exit bootstrap_retcode rescue SystemExit => sex LogFactory.ApplicationShutdown() exit( sex.status ) rescue Exception => ex g_Log.Exception( ex, "Exception during #{__FILE__}." ) puts "Exception during #{__FILE__}: #{ex.message}" puts ex.backtrace.join("\n") if ( g_Options.show_popups? ) then dlg = RSG::Base::Windows::ExceptionStackTraceDlg::new( ex, g_Config.EmailAddress, AUTHOR, EMAIL ) dlg.ShowDialog( ) end exit( -1 ) end end # bootstrap.rb