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 +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
|