require 'commands/start' require 'commands/init/init_model' require 'commands/init/changelist_model' require 'commands/init/depot_model' require 'commands/init/file_definition' require 'commands/init/group_model' require 'commands/init/system_settings_model' require 'commands/init/trigger_model' require 'commands/init/user_model' module Commands def Commands.init(options=nil) init_dir = 'p4init' p4port = '127.0.0.1:1666' version = nil auto = false if options and !options.params.empty? op = OptionParser.new do |op| op.on('-p PORT', '--port PORT', 'P4PORT setting') { |x| p4port = x } op.on('-a', '--auto', 'Force charset to be auto') { |x| auto = true } op.on('-v VERSION', '--version VERSION', 'ftp.perforce.com version') do |v| version = v end end op.parse!(options.params) if !options.params.empty? init_dir = options.params.first end end if options and !options.params.empty? init_dir = options.params.shift end initialize_p4d(init_dir, p4port, auto) end def Commands.print_init_help puts <<-END.gsub(/^ {6}/, '') p4util init [-p P4PORT] [-a] [p4_init_dir] Reads definitions in the directory p4_init_dir - by default, just 'p4init' - and then calls methods on a p4d instance assumed to be running locally. This assumes we are basically starting from scratch. If you have an existing p4d instance, it's highly likely your initialization run will fail. Delete your settings and working area and reset. Options: -p P4PORT : Specify p4 port setting, defaults to ':1666' (uses localhost) -a : Force our 'charset' to be 'auto' ## Definition Files ## Files are Ruby scripts, each basically extending a model class defined by this application. Each model type is defined separately later, however, you should have at least one User model that's marked as a super user. If you don't do this, you will always have a super user created with the login 'p4super' and the password 'superuser1A!'. ## Execution Order ## Each model you define has a 'rank'. Models classes generate instances, and each instance is sorted based on this rank. If you specify no rank, or any rank is equivalent, well, you submit your will to the gods of random. The only special model type that does not obey these rules is the SystemSettings model, which is always handled in a very particular order. ## SystemSettingsModel ## Example: class MySystemSettings < SystemSettingsModel # These are default settings @unicode = true @security_level = 0 # By default this is empty, but here's an example of usage @configure_settings = { 'dm.keys.hide' => '2' } end When `unicode` is enabled, this assumes that the `p4util init` command is run in the *same directory* as `p4util start`. ## UserModel ## Example of a super user (you need one): class SuperUser < UserModel @super = true # if you don't set, we'll just use this for the full_name and email @login = 'super' @password = 'superuser1A' end Example of a normal user: class JohnDoeUser < UserModel def rank; 100; end @login = 'jdoe' @full_name = 'John Doe' @email = 'jdoe@example.com' @password = 'johndoe1A!' end Note that with the super user, you don't really need a rank, but with your other models, it's a good idea. (You can mix when users come and go with other changes.) ## ChangelistModel ## Example of a changelist with an add and edit: class Changelist2 < ChangelistModel def rank; 1001 end @description = 'An example add and edit' @user = 'jdoe' @edits = [ FileDefinition.new(:path => 'depot/README.txt', :content => <<-STOP.gsub(/^ {8}/, '') This is an example readme. Added a second line STOP ) ] @adds = [ FileDefinition.new(:path => 'depot/main/project2/example.txt', :local_path => 'p4init/some_text.txt') ] end Note that adds an edits are specified with 'FileDefinition' objects. Each file definition instance can define text content inline, or via a 'local_path' to a file relative to the current working directory. The `@user` is not necessary, but you probably don't want to add everything as your super user, so set this to a UserModel instance that should exist at this point. ## DepotModel ## Example creating a new standard depot: class ProjectDepot < DepotModel def rank 1001 end @depot = 'my_project' @description = 'The My Project depot' end Other attributes that can be set that update the depot spec being written: :type, :address, :suffix, :map, :spec_map ## GroupModel ## Example: class SystemGroup < GroupModel def rank; 2000 end @group = 'system' @timeout = 'unset' @password_timeout = 'unset' @users = ['app'] end Attributes: :group, :max_results, :max_scan_rows, :max_lock_time, :password_timeout, :timeout, :users, :subgroups, :owners ## TriggerModel ## Example: class ExampleTriggers < TriggerModel def rank; 50; end @triggers = [ 'trig1 change-submit //depot/dir/... "/usr/bin/s1.pl %changelist%"', 'trig2 change-submit -//depot/z/... "/usr/bin/s1.pl %user%"' ] end END end private def Commands.initialize_p4d(init_dir, p4port, auto) # Go through our init_dir, and evaluate each script as if it were defined # in the Commands::Init module. if File.directory?(init_dir) Dir.glob("#{init_dir}/**/*.rb") do |file| contents = IO.read(file) Commands::Init.class_eval(contents, file) end elsif File.file?(init_dir) contents = IO.read(init_dir) Commands::Init.class_eval(contents, init_dir) end # Note that nothing is actually done until this line. This allows classes # to re-define methods and do fancy shit, like, 'oh in security_settings 0 # this guy actually doesn't have a password'. Commands::Init::InitModel.run(p4port, auto) end end