p4tools 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -0
- data/Gemfile.lock +14 -0
- data/bin/p4tools +5 -0
- data/lib/commands/move.rb +80 -0
- data/lib/commands/revert.rb +29 -0
- data/lib/commands/shelve.rb +53 -0
- data/lib/config/README +1 -0
- data/lib/p4_tools.rb +88 -0
- data/lib/p4tools/environment.rb +18 -0
- data/lib/p4tools/p4_delegate.rb +13 -0
- data/lib/p4tools/parsers/command_entry.rb +11 -0
- data/lib/p4tools/parsers/command_options.rb +32 -0
- data/lib/p4tools/parsers/command_parser.rb +78 -0
- data/lib/p4tools/parsers/trollop_custom.rb +842 -0
- data/lib/p4tools/utils/command_utils.rb +37 -0
- data/lib/p4tools/utils/utils.rb +15 -0
- data/lib/p4tools/utils/validators/shelve_validator.rb +39 -0
- data/lib/p4tools/utils/window_manager.rb +18 -0
- data/p4tools.gemspec +15 -0
- metadata +80 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/bin/p4tools
ADDED
@@ -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,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
|