p4util 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ca08621e3878ebd6ab034b9046f0ed94425ad978
4
- data.tar.gz: 5be2227361463716cd9b0870eee7b2769a079f50
3
+ metadata.gz: 45a2fa04d57ee5b6aca04b509acc1705dffb1f35
4
+ data.tar.gz: 0561143880e7350548859aa309f2c499d3ac3d70
5
5
  SHA512:
6
- metadata.gz: 8ee68971ece495b7f3d3f7695e666e3b443d08a24e26c4d8a90b1a6a83df83d786c024c1d3ab12cb7980812028afa298508fae54628411d572a33e742348b347
7
- data.tar.gz: b2f844d7ba44aabffadbdbdcd317d623d0e9b671633de45290f784ad466549bd0aa6949b14cd64ba1f6523db658e328f3ebde00b209323c1a175b39ca302a16a
6
+ metadata.gz: 1c01e49961c05d2fe79b25c504e7748349e014407ef0975c6f1dc1434b24e02a344512d840758f68601fcb3dcd434769bcd2281eb36ed5e700de064a25039292
7
+ data.tar.gz: d4e3bc148311ae182387bfe3db5541d10e5a1dae0c26fe8d8bd12a432f8749d181adfcbfc5ac27b766b18b0d38b07645f15bfd0e22267eec613254c63d8f1be2
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in p4_util.gemspec
3
+ # Specify your gem's dependencies in p4util.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -1,6 +1,20 @@
1
- # P4util
1
+ # p4util - common p4 scripting tasks for ruby projects
2
+
3
+ When building any kind of tool that uses Perforce underneath, it's pretty common
4
+ to want a few capabilities:
5
+
6
+ * Download different versions of p4, p4d, and the C++ API
7
+ * Initialize up a consistent starting environment of p4d
8
+
9
+ This is a binary script and set of rake tasks that allows this.
10
+
11
+
12
+ ## Usage
13
+
14
+ After installation, `p4util help` will list available commands, and
15
+ `p4util help [command]` will list instructions. Some commands, like `p4util init`
16
+ are quite extensive, and should be used as the reference.
2
17
 
3
- TODO: Write a gem description
4
18
 
5
19
  ## Installation
6
20
 
@@ -18,14 +32,11 @@ Or install it yourself as:
18
32
 
19
33
  $ gem install p4util
20
34
 
21
- ## Usage
22
35
 
23
- TODO: Write usage instructions here
36
+ ## Changes
24
37
 
25
- ## Contributing
38
+ * 0.1.0: Added `p4util init` command that can do some common p4d configurations,
39
+ like setting unicode and security settings, along with users and basic
40
+ adds and edits of files.
26
41
 
27
- 1. Fork it ( https://github.com/[my-github-username]/p4util/fork )
28
- 2. Create your feature branch (`git checkout -b my-new-feature`)
29
- 3. Commit your changes (`git commit -am 'Add some feature'`)
30
- 4. Push to the branch (`git push origin my-new-feature`)
31
- 5. Create a new Pull Request
42
+ * 0.0.1-0.0.3: Basic download capabilities
data/Rakefile CHANGED
@@ -1,2 +1,7 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rdoc/task'
2
3
 
4
+ RDoc::Task.new do |rdoc|
5
+ rdoc.main = 'README.md'
6
+ rdoc.rdoc_files.include('README.md', 'lib/**/*.rb')
7
+ end
data/bin/p4util CHANGED
@@ -4,6 +4,6 @@
4
4
  #
5
5
  lib_path = File.expand_path('../../lib', __FILE__)
6
6
  $LOAD_PATH.unshift(lib_path)
7
- require 'p4_util'
7
+ require 'p4util'
8
8
 
9
9
  P4Util::run ARGV
@@ -5,6 +5,8 @@ require 'net/ftp'
5
5
 
6
6
  module Commands
7
7
 
8
+ # TODO the p4ruby extconf.rb file mechanism has some logic to search the ftp
9
+ # site for things. We might also want to use HTTP
8
10
  def Commands.download(options=nil)
9
11
  version = 'r14.2'
10
12
  binary = 'p4d'
@@ -25,6 +27,8 @@ module Commands
25
27
  end
26
28
 
27
29
  case binary
30
+ when 'p4'
31
+ download_p4_via_ftp(version)
28
32
  when 'p4d'
29
33
  download_p4d_via_ftp(version)
30
34
  when 'p4api'
@@ -36,15 +40,16 @@ module Commands
36
40
 
37
41
  def Commands.print_download_help
38
42
  puts <<-END.gsub(/^ {6}/, '')
39
- p4util download [p4d|p4api]
43
+ p4util download [p4|p4d|p4api]
40
44
 
41
45
  Downloads one of the following utilities (in lieu of an installer) into
42
46
  a local work/ directory.
43
47
 
48
+ * p4
44
49
  * p4d
45
50
  * p4api
46
51
 
47
- Will default to the latest release unless you don't know what that is.
52
+ Will default to the r14.2 release.
48
53
 
49
54
  Options:
50
55
 
@@ -55,6 +60,14 @@ module Commands
55
60
 
56
61
  private
57
62
 
63
+ def Commands.download_p4_via_ftp(version)
64
+ download_via_ftp(version, OsUtil.p4_executable, OsUtil.p4_path)
65
+
66
+ if !File.executable?(OsUtil.p4_path)
67
+ File.chmod(0755, OsUtil.p4_path)
68
+ end
69
+ end
70
+
58
71
  def Commands.download_p4d_via_ftp(version)
59
72
  download_via_ftp(version, OsUtil.p4d_executable, OsUtil.p4d_path)
60
73
 
@@ -0,0 +1,101 @@
1
+ require 'commands/init/init_model'
2
+ require 'commands/init/p4_helpers'
3
+ require 'fileutils'
4
+
5
+ module Commands
6
+ module Init
7
+
8
+ class ChangelistModel < InitModel
9
+ include Commands::Init::P4Helpers
10
+
11
+ inheritable_attributes :description, :adds, :edits, :user
12
+
13
+ # Please make this descriptive
14
+ @description = 'Init changelist'
15
+
16
+ # An array of 'file definitions'
17
+ @adds = []
18
+
19
+ # An array of 'file definitions' expected to already exist
20
+ @edits = []
21
+
22
+ # The user we should operate as if you don't want to do this as the super
23
+ # user
24
+ @user = nil
25
+
26
+ #========================================================================
27
+ # Internal implementation
28
+ #========================================================================
29
+
30
+ def self.abstract
31
+ true
32
+ end
33
+
34
+ attr_accessor :description, :adds, :edits, :user
35
+
36
+ def initialize
37
+ @description = self.class.description
38
+ @adds = self.class.adds
39
+ @edits = self.class.edits
40
+ @user = self.class.user
41
+ end
42
+
43
+ def execute(p4, models, super_user)
44
+ # Set up our options with a user context if specified by searching the
45
+ # defined models.
46
+ options = {:p4 => p4}
47
+ if user
48
+ model = models.find {|m| m.class < UserModel && m.login == user }
49
+ options[:user] = model.login
50
+ options[:password] = model.password
51
+ options[:olduser] = super_user.login
52
+ options[:oldpass] = super_user.password
53
+ end
54
+
55
+ # Define the logic of what the changelist model really does: make adds
56
+ # and edits for the most part
57
+ open_client(options) do |client_path, name|
58
+
59
+ change_spec = p4.fetch_change
60
+ change_spec._description = description
61
+ results = p4.save_change(change_spec)
62
+ change_id = results[0].gsub(/^Change (\d+) created./, '\1')
63
+
64
+ @adds.each do |add|
65
+ add_path = File.join(client_path, add.path)
66
+ dir = File.dirname(add_path)
67
+ if dir && !dir.empty?
68
+ if !Dir.exist?(dir)
69
+ FileUtils.mkpath(dir)
70
+ end
71
+ end
72
+ if add.content
73
+ IO.write(add_path, add.content)
74
+ elsif add.local_path
75
+ FileUtils.copy(add.local_path, add_path)
76
+ end
77
+ p4.run_add('-c', change_id, add_path)
78
+ end
79
+
80
+ @edits.each do |edit|
81
+ edit_path = File.join(client_path, edit.path)
82
+ p4.run_edit('-c', change_id, edit_path)
83
+
84
+ if edit.content
85
+ IO.write(edit_path, edit.content)
86
+ else
87
+ FileUtils.copy(edit.local_path, edit_path)
88
+ end
89
+ end
90
+
91
+ p4.run_submit('-c', change_id)
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ # do stuff
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,16 @@
1
+
2
+ module Commands
3
+ module Init
4
+
5
+ class FileDefinition
6
+ attr_accessor :path, :content, :local_path
7
+
8
+ # Options should indicate :content or :local_path, but not both
9
+ def initialize(options)
10
+ @path = options[:path]
11
+ @content = options[:content] if options.key?(:content)
12
+ @local_path = options[:local_path] if options.key?(:local_path)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,95 @@
1
+ require 'P4'
2
+
3
+ module Commands
4
+ module Init
5
+ # Base class of 'initializers'.
6
+ #
7
+ # This actually acts more as a registry of defined classes.
8
+ class InitModel
9
+ # Child classes should define this number greater than one. 0 should be
10
+ # reserved for a single system settings instance.
11
+ def rank
12
+ 1
13
+ end
14
+
15
+ # It's likely that the main child class will handle overriding this method.
16
+ # Our default method does nothing.
17
+ def execute(p4)
18
+ end
19
+
20
+ #========================================================================
21
+ # Ok, the real business starts here.
22
+ #
23
+ # Don't even think about overriding the following rules
24
+ #========================================================================
25
+
26
+ class << self
27
+ attr_accessor :model_classes
28
+ end
29
+
30
+ # The registered set of non-abstract model classes.
31
+ #
32
+ @model_classes = []
33
+
34
+ @inheritable_attributes ||= [:inheritable_attributes]
35
+
36
+ # A lot of our settings are actually class instance variables, which
37
+ # can be overridden. This allows the child classes that depend on these
38
+ # values to define what they are.
39
+ def self.inheritable_attributes(*args)
40
+ @inheritable_attributes += args
41
+ args.each do |arg|
42
+ class_eval %(
43
+ class << self; attr_accessor :#{arg} end
44
+ )
45
+ end
46
+ @inheritable_attributes
47
+ end
48
+
49
+ def self.inherited(subclass)
50
+ # Copy 'down' any inheritable attribute to the new child class
51
+ @inheritable_attributes.each do |inheritable_attribute|
52
+ instance_var = "@#{inheritable_attribute}"
53
+ subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
54
+ end
55
+
56
+ if subclass.respond_to?(:abstract) && subclass.send(:abstract)
57
+ InitModel.model_classes.push(subclass)
58
+ end
59
+ end
60
+
61
+ def self.run(p4port)
62
+ system_settings = nil
63
+ super_user = nil
64
+ models = []
65
+
66
+ InitModel.model_classes.each do |model|
67
+ if model <= DefaultSuperUser
68
+ # do nothing
69
+ elsif model <= SystemSettingsModel
70
+ system_settings = model.new
71
+ elsif !super_user and model <= UserModel and model.super
72
+ super_user = model.new
73
+ else
74
+ models << model.new
75
+ end
76
+ end
77
+
78
+ if !super_user
79
+ super_user = DefaultSuperUser.new
80
+ end
81
+
82
+ models.sort! { |a, b| a.rank <=> b.rank }
83
+
84
+ p4 = P4.new
85
+ p4.port = p4port
86
+ p4.connect
87
+ p4.exception_level = P4::RAISE_ERRORS
88
+
89
+ system_settings.execute(p4, super_user)
90
+
91
+ models.each { |m| m.execute(p4, models, super_user) }
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,75 @@
1
+
2
+ require 'conventions'
3
+ require 'random_util'
4
+
5
+ module Commands
6
+ module Init
7
+ module P4Helpers
8
+
9
+ # Intended to be a block helper that creates a 'temporary client'
10
+ #
11
+ # Your block should take the client name and path.
12
+ #
13
+ # Example:
14
+ #
15
+ # open_client(p4) do |path, name|
16
+ # puts "my client #{name}'s root is #{path}"
17
+ # end
18
+ def open_client(options)
19
+ p4 = options[:p4]
20
+
21
+ # Switch user only if specified
22
+ user = nil
23
+ password = nil
24
+ olduser = nil
25
+ oldpass = nil
26
+ if (options[:user])
27
+ user = options[:user]
28
+ password = options[:password] if options.key?(:password)
29
+ olduser = options[:olduser]
30
+ oldpass = options[:oldpass] if options.key?(:oldpass)
31
+ p4.user = user
32
+ p4.password = password
33
+ p4.run_login if password
34
+ end
35
+
36
+ name = RandomUtil.randstr
37
+ dir = File.join(Conventions.client_root_dir, name)
38
+
39
+ if !Dir.exist?(dir)
40
+ FileUtils.mkpath(dir)
41
+ end
42
+
43
+ spec = p4.fetch_client
44
+ spec._root = dir
45
+ spec._client = name
46
+ spec._description = 'p4util init temp client'
47
+ spec._view = ["//depot/... //#{name}/depot/..."]
48
+
49
+ p4.save_client(spec)
50
+
51
+ p4.client = name
52
+
53
+ p4.run_sync('-f', '//...')
54
+
55
+ if block_given?
56
+ yield dir, name
57
+ else
58
+ return dir, name
59
+ end
60
+ ensure
61
+ if block_given?
62
+ if (user)
63
+ p4.user = olduser
64
+ p4.password = oldpass
65
+ p4.run_login if oldpass
66
+ end
67
+
68
+ p4.run_client('-d', '-f', name)
69
+ p4.client = 'invalid'
70
+ FileUtils.rmtree(dir)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,120 @@
1
+
2
+ require 'commands'
3
+ require 'commands/init/init_model'
4
+
5
+ module Commands
6
+ module Init
7
+
8
+ class SystemSettingsModel < InitModel
9
+ inheritable_attributes :unicode, :security_level, :configure_settings
10
+
11
+ # Pick some normal defaults
12
+
13
+ # This basically will upgrade the DB before anything happens to unicode
14
+ # mode. The default is false, though you probably should use true for
15
+ # most default 'setups' these days.
16
+ @unicode = false
17
+
18
+ # The security level is set basically as the first major setting, before
19
+ # most users are generated, to avoid password mayhem
20
+ @security_level = 0
21
+
22
+ # These basically map to 'p4 configure set #{key}=#{value}'
23
+ @configure_settings = {}
24
+
25
+ #========================================================================
26
+ # Internal implementation - don't mess with this
27
+ #========================================================================
28
+
29
+ def rank
30
+ 0
31
+ end
32
+
33
+ def self.abstract
34
+ true
35
+ end
36
+
37
+ attr_reader :unicode, :security_level, :configure_settings
38
+
39
+ def initialize
40
+ @unicode = self.class.unicode
41
+ @security_level = self.class.security_level
42
+ @configure_settings = self.class.configure_settings
43
+ end
44
+
45
+ # If the security level is > 0, we generate the super_user first, change
46
+ # security, then reset the super's password. The p4 connection is
47
+ # established here with whatever credentials we end up with the super
48
+ # user as.
49
+ def execute(p4, super_user)
50
+ set_unicode_mode(p4)
51
+ set_security_and_super_user(p4, super_user)
52
+ create_default_protections(p4)
53
+ end
54
+
55
+ private
56
+
57
+ def set_unicode_mode(p4)
58
+ if unicode
59
+ p4.disconnect
60
+
61
+ # Halt any exiting perforce server
62
+ Commands.kill
63
+
64
+ # Execute the upgrade of the p4d instance
65
+ Commands.unicode_upgrade
66
+
67
+ # Restart: this assumes the cwd of the init call is the same as the
68
+ # 'start' call. This is a pretty safe assumption, since our .init
69
+ # function will call start if it's not running.
70
+ Commands.start
71
+
72
+ while !Commands.p4d_running?
73
+ sleep 0.2
74
+ end
75
+
76
+ # I'm not sure this should be anything else for the purposes of
77
+ # initialization
78
+ p4.charset = 'auto'
79
+
80
+ p4.connect
81
+ end
82
+ end
83
+
84
+ def set_security_and_super_user(p4, super_user)
85
+ # update security mode if needed
86
+ if security_level > 0
87
+ # On some security levels, you *must* reset passwords when updating
88
+ # even if the base password is strong.
89
+ super_pwd = super_user.password
90
+ super_user.password = ''
91
+ super_user.execute(p4)
92
+
93
+ p4.run('configure', 'set', "security=#{security_level}")
94
+
95
+ p4.user = super_user.login
96
+ p4.password = super_user.password
97
+
98
+ p4.run_password(super_user.password, super_pwd)
99
+ super_user.password = super_pwd
100
+
101
+ p4.password = super_user.password
102
+ p4.run_login
103
+ else
104
+ # Just create the user
105
+ super_user.execute(p4)
106
+ if super_user.password
107
+ p4.password = super_user.password
108
+ p4.run_login
109
+ end
110
+ end
111
+ end
112
+
113
+ def create_default_protections(p4)
114
+ # This actually will establish the defaults with our super user as the
115
+ # only entry
116
+ results = p4.run_protect('-o')
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,84 @@
1
+ require 'commands/init/init_model'
2
+
3
+ module Commands
4
+ module Init
5
+
6
+ class UserModel < InitModel
7
+ inheritable_attributes :login, :full_name, :password, :email, :super
8
+
9
+ @login = nil
10
+ @full_name = nil
11
+ @password = nil
12
+ @email = nil
13
+ # Set this to true if you want the user to be our 'super user'. We'll
14
+ # generally need one of these for each init step. We will make sure a
15
+ # protections entry for this user exists
16
+ @super = false
17
+
18
+ #========================================================================
19
+ # Internal implementation - don't mess with this
20
+ #========================================================================
21
+
22
+ def self.abstract
23
+ true
24
+ end
25
+
26
+ attr_accessor :login, :password
27
+
28
+ def initialize
29
+ @login = self.class.login
30
+ @full_name = self.class.full_name
31
+ @password = self.class.password
32
+ @email = self.class.email
33
+ @super = self.class.super
34
+ end
35
+
36
+ def email
37
+ @email || "#{login}@example.com"
38
+ end
39
+
40
+ def full_name
41
+ @full_name || login
42
+ end
43
+
44
+ def super?
45
+ @super
46
+ end
47
+
48
+ def to_s
49
+ "UserModel: login=#{login} email=#{email} full_name=#{full_name} password=#{password}"
50
+ end
51
+
52
+ def to_spec
53
+ spec = {
54
+ 'User' => login,
55
+ 'Email' => email,
56
+ 'FullName' => full_name
57
+ }
58
+ spec
59
+ end
60
+
61
+ def execute(p4, models=nil, super_user=nil)
62
+ p4.save_user(to_spec, '-f')
63
+
64
+ p4.user = login
65
+ p4.password = ''
66
+
67
+ p4.run_password('', password) if password
68
+
69
+ if super_user
70
+ p4.user = super_user.login
71
+ p4.password = super_user.password
72
+ end
73
+ end
74
+ end
75
+
76
+ class DefaultSuperUser < UserModel
77
+ @login = 'p4super'
78
+ @password = 'superuser1A!'
79
+ @full_name = 'Super User'
80
+ @super = true
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,155 @@
1
+
2
+ require 'commands/start'
3
+ require 'commands/init/init_model'
4
+ require 'commands/init/changelist_model'
5
+ require 'commands/init/file_definition'
6
+ require 'commands/init/system_settings_model'
7
+ require 'commands/init/user_model'
8
+
9
+ module Commands
10
+
11
+ def Commands.init(options=nil)
12
+ init_dir = 'p4init'
13
+ # TODO we may want to allow this to be overridden, though I find this a
14
+ # spurious and dangerous use case
15
+ p4port = '127.0.0.1:1666'
16
+
17
+ if options and !options.params.empty?
18
+ init_dir = options.params.shift
19
+ end
20
+
21
+ if !p4d_running?
22
+ # The way this works: if you specify any options, like what to download
23
+ # you must specify the initialization directory.
24
+ Commands.start(options)
25
+ end
26
+
27
+ initialize_p4d(init_dir, p4port)
28
+ end
29
+
30
+ def Commands.print_init_help
31
+ puts <<-END.gsub(/^ {6}/, '')
32
+ p4util init [p4_init_dir]
33
+
34
+ Reads definitions in the directory p4_init_dir - by default, just
35
+ 'p4init' - and then calls methods on a p4d instance assumed to be running
36
+ locally.
37
+
38
+ This assumes we are basically starting from scratch. If you have an
39
+ existing p4d instance, it's highly likely your initialization run will
40
+ fail. Delete your settings and working area and reset.
41
+
42
+ ## Definition Files ##
43
+
44
+ Files are Ruby scripts, each basically extending a model class defined
45
+ by this application.
46
+
47
+ Each model type is defined separately later, however, you should have
48
+ at least one User model that's marked as a super user. If you don't do
49
+ this, you will always have a super user created with the login 'p4super'
50
+ and the password 'superuser1A!'.
51
+
52
+ ## Execution Order ##
53
+
54
+ Each model you define has a 'rank'. Models classes generate instances, and
55
+ each instance is sorted based on this rank. If you specify no rank, or
56
+ any rank is equivalent, well, you submit your will to the gods of random.
57
+
58
+ The only special model type that does not obey these rules is the
59
+ SystemSettings model, which is always handled in a very particular order.
60
+
61
+
62
+ ## SystemSettingsModel ##
63
+
64
+ Example:
65
+
66
+ class MySystemSettings < SystemSettingsModel
67
+ # These are default settings
68
+ @unicode = true
69
+ @security_level = 0
70
+
71
+ # By default this is empty, but here's an example of usage
72
+ @configure_settings = {
73
+ 'dm.keys.hide' => '2'
74
+ }
75
+ end
76
+
77
+ When `unicode` is enabled, this assumes that the `p4util init` command
78
+ is run in the *same directory* as `p4util start`.
79
+
80
+
81
+ ## UserModel ##
82
+
83
+ Example of a super user (you need one):
84
+
85
+ class SuperUser < UserModel
86
+ @super = true
87
+ # if you don't set, we'll just use this for the full_name and email
88
+ @login = 'super'
89
+ @password = 'superuser1A'
90
+ end
91
+
92
+ Example of a normal user:
93
+
94
+ class JohnDoeUser < UserModel
95
+ def rank; 100; end
96
+ @login = 'jdoe'
97
+ @full_name = 'John Doe'
98
+ @email = 'jdoe@example.com'
99
+ @password = 'johndoe1A!'
100
+ end
101
+
102
+ Note that with the super user, you don't really need a rank, but with
103
+ your other models, it's a good idea. (You can mix when users come and go
104
+ with other changes.)
105
+
106
+
107
+ ## ChangelistModel ##
108
+
109
+ Example of a changelist with an add and edit:
110
+
111
+ class Changelist2 < ChangelistModel
112
+ def rank; 1001 end
113
+ @description = 'An example add and edit'
114
+ @user = 'jdoe'
115
+ @edits = [
116
+ FileDefinition.new(:path => 'depot/README.txt',
117
+ :content => <<-STOP.gsub(/^ {8}/, '')
118
+ This is an example readme.
119
+ Added a second line
120
+ STOP
121
+ )
122
+ ]
123
+ @adds = [
124
+ FileDefinition.new(:path => 'depot/main/project2/example.txt',
125
+ :local_path => 'p4init/some_text.txt')
126
+ ]
127
+ end
128
+
129
+ Note that adds an edits are specified with 'FileDefinition' objects. Each
130
+ file definition instance can define text content inline, or via a
131
+ 'local_path' to a file relative to the current working directory.
132
+
133
+ The `@user` is not necessary, but you probably don't want to add everything
134
+ as your super user, so set this to a UserModel instance that should exist
135
+ at this point.
136
+
137
+ END
138
+ end
139
+
140
+ private
141
+
142
+ def Commands.initialize_p4d(init_dir, p4port)
143
+ # Go through our init_dir, and evaluate each script as if it were defined
144
+ # in the Commands::Init module.
145
+ Dir.glob("#{init_dir}/**/*.rb") do |file|
146
+ contents = IO.read(file)
147
+ Commands::Init.class_eval(contents, file)
148
+ end
149
+
150
+ # Note that nothing is actually done until this line. This allows classes
151
+ # to re-define methods and do fancy shit, like, 'oh in security_settings 0
152
+ # this guy actually doesn't have a password'.
153
+ Commands::Init::InitModel.run(p4port)
154
+ end
155
+ end
data/lib/commands/kill.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'commands/util'
1
2
  require 'sys/proctable'
2
3
 
3
4
  include Sys
@@ -5,8 +6,21 @@ include Sys
5
6
  module Commands
6
7
 
7
8
  def Commands.kill(options=nil)
8
- ProcTable.ps().find_all{|p| p.comm =~ /p4d/}
9
- .each{|p| Process.kill('TERM', p.pid)}
9
+ ProcTable.ps().find_all{|p| p.comm =~ /p4d/}.each do |p|
10
+ begin
11
+ Process.kill('TERM', p.pid)
12
+ rescue
13
+ puts "Problem killing #{p}, ignoring"
14
+ end
15
+ end
16
+
17
+ is_running = true
18
+ while is_running
19
+ is_running = p4d_running?
20
+ if is_running
21
+ sleep 0.2
22
+ end
23
+ end
10
24
  end
11
25
 
12
26
  def Commands.print_kill_help
@@ -15,8 +29,6 @@ module Commands
15
29
 
16
30
  Finds local p4d processes and kills them.
17
31
 
18
- There should be a timeout that will force kill anything that appears stuck.
19
-
20
32
  On unix machines, will probably use `ps -x` and 'p4d', then will send
21
33
  SIGTERM signals to each process.
22
34
  END
@@ -1,9 +1,9 @@
1
1
 
2
2
  module Commands
3
3
 
4
- def Commands.start(options)
4
+ def Commands.start(options=nil)
5
5
  if !File.exists?(OsUtil.p4d_path)
6
- Commands.download
6
+ Commands.download(options)
7
7
  end
8
8
  Conventions.init_p4droot_dir
9
9
  spawn_p4d
@@ -13,6 +13,14 @@ module Commands
13
13
  pid = Process.spawn("#{OsUtil.p4d_path} -r #{Conventions.p4droot_dir} "+
14
14
  "-v server=1 -L #{Conventions.p4d_log_path}")
15
15
  Process.detach(pid)
16
+
17
+ while !p4d_running?
18
+ sleep 0.2
19
+ end
20
+
21
+ while !p4d_available?
22
+ sleep 0.1
23
+ end
16
24
  end
17
25
 
18
26
  def Commands.print_start_help
@@ -0,0 +1,27 @@
1
+ require 'sys/proctable'
2
+ require 'P4'
3
+
4
+ module Commands
5
+ def Commands.p4d_running?
6
+ !ProcTable.ps().find_all { |p| p.comm =~ /p4d/ }.empty?
7
+ end
8
+
9
+ def Commands.p4d_available?(port=':1666')
10
+ begin
11
+ p4 = P4.new
12
+ p4.port = port
13
+ p4.connect
14
+ p4.disconnect
15
+ true
16
+ rescue
17
+ false
18
+ end
19
+ end
20
+
21
+ def Commands.unicode_upgrade
22
+ system("#{OsUtil.p4d_path} -r #{Conventions.p4droot_dir} "+
23
+ "-v server=1 -L #{Conventions.p4d_log_path} " +
24
+ "-xi")
25
+
26
+ end
27
+ end
data/lib/commands.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'commands/download'
2
+ require 'commands/init'
2
3
  require 'commands/help'
3
4
  require 'commands/kill'
4
5
  require 'commands/start'
data/lib/conventions.rb CHANGED
@@ -17,6 +17,10 @@ module Conventions
17
17
  File.expand_path(File.join(working_dir,'p4droot'))
18
18
  end
19
19
 
20
+ def Conventions.client_root_dir
21
+ File.expand_path(File.join(working_dir,'clients'))
22
+ end
23
+
20
24
  def Conventions.init_p4droot_dir
21
25
  if !File.directory?(p4droot_dir)
22
26
  FileUtils::makedirs(p4droot_dir)
data/lib/osutil.rb CHANGED
@@ -15,6 +15,18 @@ module OsUtil
15
15
  File.expand_path(File.join(Conventions.working_dir, OsUtil.p4d_executable))
16
16
  end
17
17
 
18
+ def OsUtil.p4_executable
19
+ if windows?
20
+ 'p4.exe'
21
+ else
22
+ 'p4'
23
+ end
24
+ end
25
+
26
+ def OsUtil.p4_path
27
+ File.expand_path(File.join(Conventions.working_dir, OsUtil.p4_executable))
28
+ end
29
+
18
30
  def OsUtil.osx?
19
31
  RbConfig::CONFIG['host_os'] =~ /darwin/
20
32
  end
@@ -0,0 +1,94 @@
1
+ # p4util/task.rb - Rake tasks
2
+
3
+ require 'commands'
4
+ require 'conventions'
5
+ require 'fileutils'
6
+ require 'ostruct'
7
+ require 'rake/tasklib'
8
+
9
+ module P4Util
10
+ # Creates a few tasks to allow launching init and kill commands via rake
11
+ # tasks, which should make it easy to script with test tasks, for example.
12
+ #
13
+ # Example:
14
+ #
15
+ # require 'p4util/tasks'
16
+ #
17
+ # P4Util::Tasks.new do |p4util|
18
+ # p4util.version = 'r14.2' # Indicate p4d version to download
19
+ # end
20
+ #
21
+ # Tasks:
22
+ # - p4init
23
+ # - p4kill
24
+ # - p4reset
25
+ class Tasks < Rake::TaskLib
26
+ # The task base name, defaults to ':p4'
27
+ attr_accessor :basename
28
+
29
+ # P4 Version to use, defaults to 'r14.2'
30
+ attr_accessor :version
31
+
32
+ # The directory containing p4 init scripts, defaults to 'p4init'
33
+ attr_accessor :p4_init_dir
34
+
35
+ def initialize basename = :p4
36
+ @basename = basename
37
+ @version = 'r14.2'
38
+ @p4_init_dir = 'p4init'
39
+
40
+ yield self if block_given?
41
+
42
+ define_tasks
43
+ end
44
+
45
+ # Create the tasks defined by this task library
46
+ def define_tasks
47
+ desc init_task_description
48
+ task init_task_name do
49
+ options = OpenStruct.new
50
+ options.params = [p4_init_dir, '--version', version]
51
+ Commands.init(options)
52
+ end
53
+
54
+ desc kill_task_description
55
+ task kill_task_name do
56
+ options = OpenStruct.new
57
+ options.params = ['--version', version]
58
+ Commands.kill(options)
59
+ end
60
+
61
+ desc reset_task_description
62
+ task reset_task_name => kill_task_name do
63
+ FileUtils.rmtree(Conventions.p4droot_dir)
64
+ end
65
+
66
+ self
67
+ end
68
+
69
+ def init_task_description
70
+ "Initializes a p4d instance, and ensures it's downloaded and running"
71
+ end
72
+
73
+ def init_task_name
74
+ "#{basename}init"
75
+ end
76
+
77
+ def kill_task_description
78
+ 'Halt any locally running p4d instance'
79
+ end
80
+
81
+ def kill_task_name
82
+ "#{basename}kill"
83
+ end
84
+
85
+ def reset_task_description
86
+ 'Cleans out the current p4droot working directory (after killing p4d)'
87
+ end
88
+
89
+ def reset_task_name
90
+ "#{basename}reset"
91
+ end
92
+ end
93
+ end
94
+
@@ -0,0 +1,3 @@
1
+ module P4Util
2
+ VERSION = '0.1.0'
3
+ end
@@ -1,4 +1,4 @@
1
- require 'p4_util/version'
1
+ require 'p4util/version'
2
2
  require 'commands'
3
3
  require 'optparse'
4
4
  require 'ostruct'
@@ -0,0 +1,7 @@
1
+
2
+ module RandomUtil
3
+
4
+ def RandomUtil.randstr(len=8)
5
+ (0...len).map { (65 + rand(26)).chr }.join
6
+ end
7
+ end
data/p4init/287.jpg ADDED
Binary file
@@ -0,0 +1,36 @@
1
+
2
+ class Changelist1 < ChangelistModel
3
+ def rank; 1000 end
4
+ @description = 'A couple of new files'
5
+ @adds = [
6
+ FileDefinition.new(:path => 'depot/README.txt',
7
+ :content => <<-END.gsub(/^ {8}/, '')
8
+ This is an example readme.
9
+ END
10
+ ),
11
+ FileDefinition.new(:path => 'depot/dev/memorabilia/kitty.jpg',
12
+ :local_path => 'p4init/287.jpg'
13
+ )
14
+ ]
15
+ end
16
+
17
+ class Changelist2 < ChangelistModel
18
+ def rank; 1001 end
19
+ @description = 'An example add and edit'
20
+ @user = 'jdoe'
21
+ @edits = [
22
+ FileDefinition.new(:path => 'depot/README.txt',
23
+ :content => <<-END.gsub(/^ {8}/, '')
24
+ This is an example readme.
25
+ Added a second line
26
+ END
27
+ )
28
+ ]
29
+ @adds = [
30
+ FileDefinition.new(:path => 'depot/main/project2/example.txt',
31
+ :content => <<-END.gsub(/^ {8}/, '')
32
+ Example text file.
33
+ END
34
+ )
35
+ ]
36
+ end
@@ -0,0 +1,4 @@
1
+
2
+ class TestSystemSettings < SystemSettingsModel
3
+ @unicode = true
4
+ end
@@ -0,0 +1,15 @@
1
+ # User ranks should generally be 10-100, creating them early makes sense.
2
+
3
+ class TestSuperUser < UserModel
4
+ @login = 'p4super'
5
+ @password = 'superuser1A!'
6
+ @full_name = 'Super User'
7
+ @super = true
8
+ end
9
+
10
+ class JohnDoeUser < UserModel
11
+ def rank; 10 end
12
+ @login = 'jdoe'
13
+ @full_name = 'John Doe'
14
+ @password = 'johndoe1A!'
15
+ end
data/p4util.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'p4_util/version'
4
+ require 'p4util/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'p4util'
@@ -19,7 +19,8 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.add_development_dependency 'bundler', '~> 1.7'
22
- spec.add_development_dependency 'rake', '~> 10.0'
23
22
 
24
23
  spec.add_runtime_dependency 'sys-proctable', '~> 0.9'
24
+ spec.add_runtime_dependency 'p4ruby', '2014.2.0.pre1'
25
+ spec.add_runtime_dependency 'rake', '~> 10.3'
25
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: p4util
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tristan Juricek
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-23 00:00:00.000000000 Z
11
+ date: 2014-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -25,33 +25,47 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.7'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: sys-proctable
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
34
- type: :development
33
+ version: '0.9'
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '0.9'
41
41
  - !ruby/object:Gem::Dependency
42
- name: sys-proctable
42
+ name: p4ruby
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 2014.2.0.pre1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 2014.2.0.pre1
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '0.9'
61
+ version: '10.3'
48
62
  type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '0.9'
68
+ version: '10.3'
55
69
  description: The p4util command itself provides other commands, see 'p4util help'
56
70
  after install. This allows you to things like download a p4d, start it, kill it,
57
71
  etc mostly for quick setup of testing systems.
@@ -70,12 +84,26 @@ files:
70
84
  - "./lib/commands.rb"
71
85
  - "./lib/commands/download.rb"
72
86
  - "./lib/commands/help.rb"
87
+ - "./lib/commands/init.rb"
88
+ - "./lib/commands/init/changelist_model.rb"
89
+ - "./lib/commands/init/file_definition.rb"
90
+ - "./lib/commands/init/init_model.rb"
91
+ - "./lib/commands/init/p4_helpers.rb"
92
+ - "./lib/commands/init/system_settings_model.rb"
93
+ - "./lib/commands/init/user_model.rb"
73
94
  - "./lib/commands/kill.rb"
74
95
  - "./lib/commands/start.rb"
96
+ - "./lib/commands/util.rb"
75
97
  - "./lib/conventions.rb"
76
98
  - "./lib/osutil.rb"
77
- - "./lib/p4_util.rb"
78
- - "./lib/p4_util/version.rb"
99
+ - "./lib/p4util.rb"
100
+ - "./lib/p4util/tasks.rb"
101
+ - "./lib/p4util/version.rb"
102
+ - "./lib/random_util.rb"
103
+ - "./p4init/287.jpg"
104
+ - "./p4init/test_changelists.rb"
105
+ - "./p4init/test_system_settings.rb"
106
+ - "./p4init/test_users.rb"
79
107
  - "./p4util.gemspec"
80
108
  - bin/p4util
81
109
  homepage: http://perforce.com
@@ -1,3 +0,0 @@
1
- module P4Util
2
- VERSION = "0.0.3"
3
- end