p4tools 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # A sample Gemfile
2
+ source "https://rubygems.org"
3
+
4
+ gem 'rautomation', '0.14.1'
5
+ gem 'P4Ruby-mingwx86', '~> 2014.1'
data/Gemfile.lock ADDED
@@ -0,0 +1,14 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ P4Ruby-mingwx86 (2014.1)
5
+ ffi (1.9.0-x86-mingw32)
6
+ rautomation (0.14.1)
7
+ ffi (= 1.9.0)
8
+
9
+ PLATFORMS
10
+ x86-mingw32
11
+
12
+ DEPENDENCIES
13
+ P4Ruby-mingwx86 (~> 2014.1)
14
+ rautomation (= 0.14.1)
data/bin/p4tools ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'p4_tools'
4
+
5
+ P4Tools.run(ARGV)
@@ -0,0 +1,80 @@
1
+ module P4Tools
2
+ class Move
3
+ include CommandUtils
4
+
5
+ def self.run(arguments)
6
+ Move.new(arguments).run
7
+ end
8
+
9
+ def self.set_options(opts)
10
+ opts.set do
11
+ help 'Move a pending changelist from one workspace to another, optionally shelve and delete them before.'
12
+ help 'The changelist need to be empty to move(only shelved files allowed)!'
13
+ help ''
14
+ help 'Options:'
15
+ help ''
16
+ arg :workspace, "Name of the new workspace. If 'switch' option provided, this will be ignored.", :short => '-w', :type => :string
17
+ arg :switch, 'Switch between workspaces, only 2 workspace name allowed.', :short => '-i', :type => :strings
18
+ arg :changelist, 'Changelist number to move.', :short => '-c', :type => :int, :required => true
19
+ arg :revert, 'Revert before move.', :short => '-r'
20
+ arg :shelve, 'Shelve before move.', :short => '-s'
21
+ end
22
+ end
23
+
24
+
25
+ def initialize(args)
26
+ @args = args
27
+ @p4 = P4Tools.connection
28
+ @changelist = @args[:changelist]
29
+ @is_not_empty = !empty_changelist?(@changelist)
30
+ end
31
+
32
+ def run
33
+ shelve
34
+ revert
35
+
36
+ changelist_spec = @p4.fetch_change(@changelist)
37
+ changelist_spec['Client'] = get_workspace(changelist_spec)
38
+ @p4.save_change(changelist_spec)
39
+ end
40
+
41
+
42
+ private
43
+
44
+ def shelve
45
+ if @args[:shelve] && @is_not_empty
46
+ @p4.run_shelve('-f', '-c', @changelist)
47
+ end
48
+ end
49
+
50
+ def revert
51
+ if @args[:revert] && @is_not_empty && all_files_shelved?(@changelist, true)
52
+ @p4.run_revert('-w', '-c', @changelist, '//...')
53
+ end
54
+ end
55
+
56
+ def get_workspace(changelist_spec)
57
+ if @args[:switch]
58
+ current = changelist_spec['Client']
59
+ workspaces = @args[:switch]
60
+
61
+ validate_workspaces(workspaces, current)
62
+
63
+ workspaces.delete(current)
64
+ workspaces.first
65
+ else
66
+ @args[:workspace]
67
+ end
68
+ end
69
+
70
+ def validate_workspaces(workspaces, current)
71
+ if workspaces.length != 2
72
+ raise(ArgumentError, 'The switch parameter need to contains 2 workspace names exactly!')
73
+ end
74
+
75
+ unless workspaces.include?(current)
76
+ raise(ArgumentError, "The switch parameter does not contains the currently active workspace: #{current}!")
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,29 @@
1
+ module P4Tools
2
+ class Revert
3
+ def self.run(arguments)
4
+ p4 = P4Tools.connection
5
+ parameters = []
6
+ parameters.push('-w') if arguments[:delete_added_files]
7
+
8
+ if arguments[:changelist]
9
+ parameters.push('-c').push(arguments[:changelist]).push('//...')
10
+ else
11
+ parameters.push(*arguments[:files])
12
+ end
13
+
14
+ p4.run_revert(parameters)
15
+ end
16
+
17
+ def self.set_options(opts)
18
+ opts.set do
19
+ help 'Revert the given files or all file in the changelist and optionally delete the added files from the disk too.'
20
+ help ''
21
+ help 'Options:'
22
+ help ''
23
+ arg :delete_added_files, 'Delete added files.', :short => '-d'
24
+ arg :changelist, 'Changelist number.', :short => '-c', :type => :int
25
+ arg :files, 'The absolute path of the files to delete.', :short => '-f', :type => :strings
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,53 @@
1
+ module P4Tools
2
+ class Shelve
3
+ include CommandUtils
4
+
5
+ def self.run(arguments)
6
+ Shelve.new(arguments).run
7
+ end
8
+
9
+ def self.set_options(opts)
10
+ opts.set do
11
+ help 'Shelve all files to the given changelist or create a new one if not provided.'
12
+ help ''
13
+ help 'Options:'
14
+ help ''
15
+ arg :files, 'The absolute path of the files to shelve.', :short => '-f', :type => :strings, :required => true
16
+ arg :changelist, 'The changelist number to shelve, if not given, then create a new one.', :short => '-c', :type => :int
17
+ end
18
+ end
19
+
20
+
21
+ def initialize(args)
22
+ @files = args[:files]
23
+ @shelve_changelist = args[:changelist] || create_new_changelist("Shelve container for files:\n\n#{@files.join("\n")}")
24
+ @p4 = P4Tools.connection
25
+ end
26
+
27
+ def run
28
+ current_changelist = get_current_changelist
29
+
30
+ move_to(@shelve_changelist)
31
+ shelve
32
+ move_to(current_changelist)
33
+ end
34
+
35
+
36
+ private
37
+
38
+ def get_current_changelist
39
+ file_info = @p4.run_opened('-u', @p4.user, @files.first).first
40
+ file_info['change']
41
+ end
42
+
43
+ def shelve
44
+ @p4.run_shelve('-f', '-c', @shelve_changelist, @files)
45
+ end
46
+
47
+ def move_to(changelist)
48
+ @p4.run_reopen('-c', changelist, @files)
49
+ end
50
+
51
+ end
52
+
53
+ end
data/lib/config/README ADDED
@@ -0,0 +1 @@
1
+ Put your 'p4.ini' file to this folder to load automatically during script run.
data/lib/p4_tools.rb ADDED
@@ -0,0 +1,88 @@
1
+ require 'P4'
2
+ require_relative 'p4tools/environment'
3
+ require_relative 'p4tools/p4_delegate'
4
+ require_relative 'p4tools/utils/utils'
5
+ require_relative 'p4tools/utils/command_utils'
6
+ require_relative 'p4tools/utils/window_manager'
7
+ require_relative 'p4tools/parsers/command_parser'
8
+ require_relative 'p4tools/parsers/trollop_custom'
9
+ require_relative 'p4tools/parsers/command_options'
10
+ require_relative 'p4tools/parsers/command_entry'
11
+
12
+ module P4Tools
13
+
14
+ # @param [Array<String>] args
15
+ # @return [void]
16
+ def self.run(args=ARGV)
17
+ entries = CommandParser.new(args).parse
18
+ global_arguments = entries.shift.arguments
19
+
20
+ setup_perforce_config(global_arguments[:p4config])
21
+ create_perforce_connection
22
+
23
+ begin
24
+ run_commands(entries)
25
+ WindowManager.refresh if global_arguments[:refresh]
26
+ ensure
27
+ @p4.disconnect
28
+ end
29
+ end
30
+
31
+ # @param [CommandOptions] opts
32
+ # @return [void]
33
+ def self.set_options(opts)
34
+ opts.set do
35
+ help 'Simple command line tool to run custom perforce actions through subcommands.'
36
+ help 'For more information, please check the help page of the command.'
37
+ help ''
38
+ help "Available subcommands are:\n #{SUB_COMMANDS.join("\n ")}"
39
+ help ''
40
+ help 'Global options:'
41
+ help ''
42
+ arg :refresh, 'Send a refresh keystroke(F5) to the Visual Client.', :short => '-r'
43
+ arg :p4config, 'Absolute path of the P4CONFIG file.', :short => '-p', :type => :string
44
+ end
45
+ end
46
+
47
+ # @return [P4]
48
+ def self.connection
49
+ @p4
50
+ end
51
+
52
+
53
+ class << self
54
+ private
55
+
56
+ # @return [void]
57
+ def create_perforce_connection
58
+ @p4 = P4.new
59
+ @p4.connect
60
+ end
61
+
62
+ # @param [Hash<Module, Hash<Symbol, Object>>] commands
63
+ # @return [void]
64
+ def run_commands(entries)
65
+ entries.each do |entry|
66
+ command = entry.command
67
+ arguments = entry.arguments
68
+
69
+ command.run(arguments)
70
+ end
71
+ end
72
+
73
+ # @param [String] config_path
74
+ # @return [void]
75
+ def setup_perforce_config(config_path)
76
+ if config_path
77
+ ENV['P4CONFIG'] = config_path
78
+ elsif ENV['P4CONFIG'].nil?
79
+ ENV['P4CONFIG'] = CONFIG_ROOT + '/p4.ini'
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ if __FILE__ == $PROGRAM_NAME
86
+ run
87
+ end
88
+ end
@@ -0,0 +1,18 @@
1
+ # The absolute path of the main script(p4_tools.rb)
2
+ ROOT = File.expand_path('..', File.dirname(__FILE__))
3
+ # The absolute path of the folder which contains the command files
4
+ COMMANDS_ROOT = ROOT + '/commands'
5
+ # The absolute path of the folder which contains the custom command files
6
+ CUSTOM_COMMANDS_ROOT = ROOT + '/commands/custom'
7
+ # The absolute path of the folder which contains the configuration files
8
+ CONFIG_ROOT = ROOT + '/config'
9
+ # Special command to simply delegate everything to p4.run
10
+ P4_COMMAND = 'p4'
11
+ # Array of command names, read from the COMMAND_ROOT folder
12
+ SUB_COMMANDS = Dir[COMMANDS_ROOT + '/*.rb'].collect { |file|
13
+ File.basename(file, '.rb')
14
+ }
15
+ SUB_COMMANDS.push(P4_COMMAND)
16
+
17
+
18
+ $LOAD_PATH.unshift(CUSTOM_COMMANDS_ROOT, COMMANDS_ROOT)
@@ -0,0 +1,13 @@
1
+ module P4Tools
2
+ class P4Delegate
3
+
4
+ def self.run(arguments)
5
+ @p4 = P4Tools.connection
6
+ @p4.run(arguments[:raw])
7
+ end
8
+
9
+ def self.set_options(opts)
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module P4Tools
2
+ class CommandEntry
3
+ attr_reader :command, :arguments
4
+
5
+ def initialize(command, arguments)
6
+ @command = command
7
+ @arguments = arguments
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ module P4Tools
2
+ class CommandOptions
3
+
4
+ attr_reader :show_help
5
+
6
+ def initialize(parser)
7
+ @parser = parser
8
+ @show_help = true
9
+ end
10
+
11
+ # @param [String] text
12
+ def help(text)
13
+ @parser.banner(text)
14
+ end
15
+
16
+ # @param [Symbol] name
17
+ # @param [String] desc
18
+ # @param [Hash<Symbol, Object>] options
19
+ def arg(name, desc='', options={})
20
+ @parser.opt(name, desc, options)
21
+ end
22
+
23
+ def allow_empty_args
24
+ @show_help = false
25
+ end
26
+
27
+ def set(&opts)
28
+ self.instance_eval &opts
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,78 @@
1
+ module P4Tools
2
+ class CommandParser
3
+
4
+ # @param [Array<String>] args
5
+ def initialize(args)
6
+ @raw_args = args
7
+ @parsed_args = []
8
+ end
9
+
10
+ # @return [Array<CommandEntry>]
11
+ def parse
12
+ parse_global_arguments
13
+ parse_commands
14
+
15
+ @parsed_args
16
+ end
17
+
18
+ private
19
+
20
+ # @return [void]
21
+ def parse_global_arguments
22
+ entry = CommandEntry.new(P4Tools, parse_arguments(P4Tools))
23
+ @parsed_args.push(entry)
24
+ end
25
+
26
+ # @return [void]
27
+ def parse_commands
28
+ while command = @raw_args.shift
29
+ if command == P4_COMMAND
30
+ entry = CommandEntry.new(P4Delegate, parse_p4_arguments)
31
+ @parsed_args.push(entry)
32
+ else
33
+ command_module = load_module_for_command(command)
34
+ entry = CommandEntry.new(command_module, parse_arguments(command_module))
35
+ @parsed_args.push(entry)
36
+ end
37
+ end
38
+ end
39
+
40
+ # @param [Module] command_module
41
+ # @return [Hash<Symbol, Object>]
42
+ def parse_arguments(command_module)
43
+ parser = Trollop::Parser.new
44
+ options = CommandOptions.new(parser)
45
+
46
+ command_module.set_options(options)
47
+ parser.stop_on SUB_COMMANDS
48
+
49
+ Trollop.with_standard_exception_handling(parser) {
50
+ raise Trollop::HelpNeeded if ARGV.empty? && options.show_help
51
+ parser.parse @raw_args
52
+ }
53
+ end
54
+
55
+ def parse_p4_arguments
56
+ args = []
57
+ i = 0
58
+ current = @raw_args[i]
59
+
60
+ begin
61
+ i += 1
62
+ args.push(current)
63
+ current = @raw_args[i]
64
+ end while !SUB_COMMANDS.include?(current) && i < @raw_args.length
65
+
66
+ @raw_args = @raw_args[i .. -1]
67
+ {:raw => args}
68
+ end
69
+
70
+ # @param [String] command
71
+ # @return [Module]
72
+ def load_module_for_command(command)
73
+ require command
74
+ P4Tools.const_get(Utils.classify(command))
75
+ end
76
+
77
+ end
78
+ end