p4tools 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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