gerrit-cli 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/LICENSE +7136 -0
- data/README.md +29 -0
- data/Rakefile +9 -0
- data/bin/gerrit +14 -0
- data/gerrit-cli.gemspec +22 -0
- data/lib/gerrit/cli/command/base.rb +52 -0
- data/lib/gerrit/cli/command/clone.rb +81 -0
- data/lib/gerrit/cli/command/help.rb +49 -0
- data/lib/gerrit/cli/command/push.rb +60 -0
- data/lib/gerrit/cli/constants.rb +5 -0
- data/lib/gerrit/cli/dispatcher.rb +51 -0
- data/lib/gerrit/cli/errors.rb +6 -0
- data/lib/gerrit/cli/shell_runner.rb +37 -0
- data/lib/gerrit/cli/util.rb +34 -0
- data/lib/gerrit/cli/version.rb +5 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/util_spec.rb +27 -0
- metadata +96 -0
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Gerrit::Cli
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'gerrit-cli'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install gerrit-cli
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/gerrit
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
4
|
+
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
require 'gerrit/cli/dispatcher'
|
8
|
+
|
9
|
+
|
10
|
+
logger = Logger.new(STDOUT)
|
11
|
+
logger.formatter = proc {|severity, datetime, progname, msg| "#{msg}\n" }
|
12
|
+
|
13
|
+
dispatcher = Gerrit::Cli::Dispatcher.new(logger)
|
14
|
+
dispatcher.run_command(ARGV)
|
data/gerrit-cli.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Copyright (C) 2012 VMware, Inc. All rights reserved.
|
2
|
+
require File.expand_path('../lib/gerrit/cli/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["VMware"]
|
6
|
+
gem.email = ["support@vmware.com"]
|
7
|
+
gem.description = "This provides a tool for easing common interactions" \
|
8
|
+
+ " with Gerrit. It is mostly orthogonal to the `repo'" \
|
9
|
+
+ " tool and tries not to interfere with your workflow."
|
10
|
+
gem.summary = "A simple cli for interacting with Gerrit."
|
11
|
+
gem.homepage = "http://www.cloudfoundry.org"
|
12
|
+
|
13
|
+
gem.files = Dir.glob("**/*")
|
14
|
+
gem.executables = ["gerrit"]
|
15
|
+
gem.test_files = Dir.glob("spec/**/*")
|
16
|
+
gem.name = "gerrit-cli"
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.version = Gerrit::Cli::VERSION
|
19
|
+
|
20
|
+
gem.add_development_dependency("rake")
|
21
|
+
gem.add_development_dependency("rspec")
|
22
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
module Gerrit
|
5
|
+
module Cli
|
6
|
+
module Command
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Gerrit::Cli::Command::Base
|
12
|
+
|
13
|
+
attr_reader :option_parser
|
14
|
+
|
15
|
+
def initialize(logger)
|
16
|
+
@logger = logger
|
17
|
+
@option_parser = OptionParser.new
|
18
|
+
|
19
|
+
setup_option_parser
|
20
|
+
end
|
21
|
+
|
22
|
+
def setup_option_parser
|
23
|
+
@option_parser.on('-h', '--help', 'Display usage') do
|
24
|
+
show_usage
|
25
|
+
exit 0
|
26
|
+
end
|
27
|
+
|
28
|
+
@option_parser.on('-v', '--verbose', 'Show debugging information') do
|
29
|
+
@logger.level = Logger::DEBUG
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def run(argv)
|
34
|
+
raise NotImplementedError
|
35
|
+
end
|
36
|
+
|
37
|
+
def name
|
38
|
+
self.class.name.split('::').last.downcase
|
39
|
+
end
|
40
|
+
|
41
|
+
def summary
|
42
|
+
@option_parser.banner
|
43
|
+
end
|
44
|
+
|
45
|
+
def usage
|
46
|
+
"Usage: gerrit #{name} [options]\n\n" + @option_parser.help
|
47
|
+
end
|
48
|
+
|
49
|
+
def show_usage
|
50
|
+
@logger.info(usage())
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
require 'gerrit/cli/command/base'
|
4
|
+
require 'gerrit/cli/constants'
|
5
|
+
require 'gerrit/cli/errors'
|
6
|
+
|
7
|
+
# A thin wrapper around git-clone that attempts to install commit-msg hooks
|
8
|
+
# that automatically insert the ChangeID lines used by Gerrit.
|
9
|
+
class Gerrit::Cli::Command::Clone < Gerrit::Cli::Command::Base
|
10
|
+
def initialize(logger, runner)
|
11
|
+
super(logger)
|
12
|
+
|
13
|
+
@runner = runner
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup_option_parser
|
17
|
+
super
|
18
|
+
|
19
|
+
@option_parser.banner =
|
20
|
+
"Clone a Gerrit hosted repo and install commit-msg hooks."
|
21
|
+
end
|
22
|
+
|
23
|
+
def usage
|
24
|
+
"Usage: gerrit clone [options] <repo> [<dir>]\n\n" + @option_parser.help
|
25
|
+
end
|
26
|
+
|
27
|
+
def run(argv)
|
28
|
+
args = @option_parser.parse(argv)
|
29
|
+
|
30
|
+
repo_uri, repo_dir = nil, nil
|
31
|
+
case args.length
|
32
|
+
when 2
|
33
|
+
repo_uri, repo_dir = args
|
34
|
+
when 1
|
35
|
+
repo_uri = args[0]
|
36
|
+
else
|
37
|
+
raise Gerrit::Cli::UsageError.new("Incorrect number of arguments")
|
38
|
+
end
|
39
|
+
|
40
|
+
@runner.system!("git clone #{repo_uri} #{repo_dir}")
|
41
|
+
|
42
|
+
# At this point the uri must be valid, otherwise the clone would have
|
43
|
+
# failed
|
44
|
+
parsed_repo_uri = URI.parse(repo_uri)
|
45
|
+
|
46
|
+
unless repo_dir
|
47
|
+
if parsed_repo_uri.path =~ /\/([^\/]+?)(.git)?$/
|
48
|
+
repo_dir = $1
|
49
|
+
else
|
50
|
+
emsg = "Failed to determine the directory the repo was cloned into."
|
51
|
+
raise Gerrit::Cli::Error.new(emsg)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
install_commit_hooks(parsed_repo_uri, repo_dir)
|
56
|
+
install_tracked_hooks(repo_dir)
|
57
|
+
end
|
58
|
+
|
59
|
+
def install_commit_hooks(parsed_repo_uri, repo_dir)
|
60
|
+
hook_src = "#{parsed_repo_uri.host}:hooks/commit-msg"
|
61
|
+
if parsed_repo_uri.user
|
62
|
+
hook_src = "#{parsed_repo_uri.user}@#{hook_src}"
|
63
|
+
end
|
64
|
+
|
65
|
+
hook_dst = "#{repo_dir}/.git/hooks"
|
66
|
+
|
67
|
+
gerrit_port = parsed_repo_uri.port || Gerrit::Cli::DEFAULT_GERRIT_PORT
|
68
|
+
|
69
|
+
@logger.info("\nInstalling commit-msg hooks into '#{hook_dst}'.")
|
70
|
+
@runner.system!("scp -p -P #{gerrit_port} #{hook_src} #{hook_dst}")
|
71
|
+
end
|
72
|
+
|
73
|
+
def install_tracked_hooks(repo_dir)
|
74
|
+
Dir.chdir(repo_dir) do
|
75
|
+
if File.executable?("git/install-hook-symlinks")
|
76
|
+
@logger.info("\nInstalling tracked git hooks: ")
|
77
|
+
@runner.system!("git/install-hook-symlinks")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'gerrit/cli/command/base'
|
2
|
+
require 'gerrit/cli/errors'
|
3
|
+
require 'gerrit/cli/util'
|
4
|
+
|
5
|
+
class Gerrit::Cli::Command::Help < Gerrit::Cli::Command::Base
|
6
|
+
|
7
|
+
attr_reader :commands_summary
|
8
|
+
|
9
|
+
def initialize(logger, commands)
|
10
|
+
super(logger)
|
11
|
+
|
12
|
+
@commands = commands.dup.merge(self.name => self)
|
13
|
+
|
14
|
+
rows = @commands.keys.sort.map {|k| [k, @commands[k].summary] }
|
15
|
+
@commands_summary = Gerrit::Cli::Util.render_table(rows,
|
16
|
+
:delimiter => ' ')
|
17
|
+
end
|
18
|
+
|
19
|
+
def setup_option_parser
|
20
|
+
super
|
21
|
+
|
22
|
+
@option_parser.banner =
|
23
|
+
"Show a list of commands or display help for a specific command."
|
24
|
+
end
|
25
|
+
|
26
|
+
def usage
|
27
|
+
"Usage: gerrit help [options] [<command>]\n\n" \
|
28
|
+
+ @option_parser.help \
|
29
|
+
+ "\nAvailable commands:\n" \
|
30
|
+
+ @commands_summary
|
31
|
+
end
|
32
|
+
|
33
|
+
def run(argv)
|
34
|
+
args = @option_parser.parse(argv)
|
35
|
+
|
36
|
+
case args.length
|
37
|
+
when 1
|
38
|
+
if command = @commands[args[0]]
|
39
|
+
command.show_usage
|
40
|
+
else
|
41
|
+
raise Gerrit::Cli::UsageError.new("Unknown command '#{args[0]}'")
|
42
|
+
end
|
43
|
+
when 0
|
44
|
+
show_usage
|
45
|
+
else
|
46
|
+
raise Gerrit::Cli::UsageError.new("Too many arguments")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
require 'gerrit/cli/command/base'
|
4
|
+
require 'gerrit/cli/constants'
|
5
|
+
require 'gerrit/cli/errors'
|
6
|
+
|
7
|
+
# Posts reviews to Gerrit. Assumes that your repo has at least one Gerrit
|
8
|
+
# remote.
|
9
|
+
class Gerrit::Cli::Command::Push < Gerrit::Cli::Command::Base
|
10
|
+
def initialize(logger, runner)
|
11
|
+
super(logger)
|
12
|
+
|
13
|
+
@branch = "master"
|
14
|
+
@runner = runner
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup_option_parser
|
18
|
+
super
|
19
|
+
|
20
|
+
@option_parser.banner =
|
21
|
+
"Post changes from the current branch to Gerrit for review."
|
22
|
+
|
23
|
+
@option_parser.on('-b', '--branch BRANCH',
|
24
|
+
"The remote branch these changes should be merged into." \
|
25
|
+
+ "Master is assumed by default.") do |branch|
|
26
|
+
@branch = branch
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def usage
|
31
|
+
"Usage: gerrit push [options] [<remote>]\n\n" + @option_parser.help
|
32
|
+
end
|
33
|
+
|
34
|
+
def run(argv)
|
35
|
+
args = @option_parser.parse(argv)
|
36
|
+
|
37
|
+
remote = nil
|
38
|
+
case args.length
|
39
|
+
when 1
|
40
|
+
remote = args[0]
|
41
|
+
when 0
|
42
|
+
remote = "origin"
|
43
|
+
else
|
44
|
+
raise Gerrit::Cli::UsageError.new("Incorrect number of arguments")
|
45
|
+
end
|
46
|
+
|
47
|
+
topic = get_current_branch()
|
48
|
+
|
49
|
+
cmd = ["git push",
|
50
|
+
remote,
|
51
|
+
"HEAD:refs/for/#{@branch}/#{topic}"].join(" ")
|
52
|
+
@runner.system!(cmd)
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_current_branch
|
56
|
+
@logger.debug("Getting current branch")
|
57
|
+
output = @runner.capture!("git symbolic-ref HEAD")
|
58
|
+
output.gsub(/^refs\/heads\//,"")
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'gerrit/cli/shell_runner'
|
3
|
+
require 'gerrit/cli/command/clone'
|
4
|
+
require 'gerrit/cli/command/help'
|
5
|
+
require 'gerrit/cli/command/push'
|
6
|
+
require 'gerrit/cli/errors'
|
7
|
+
|
8
|
+
module Gerrit
|
9
|
+
module Cli
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Gerrit::Cli::Dispatcher
|
14
|
+
def initialize(logger=nil)
|
15
|
+
@logger = logger || Logger.new(STDOUT)
|
16
|
+
@logger.level = Logger::INFO
|
17
|
+
|
18
|
+
runner = Gerrit::Cli::ShellRunner.new(logger)
|
19
|
+
|
20
|
+
@commands = {
|
21
|
+
'clone' => Gerrit::Cli::Command::Clone.new(logger, runner),
|
22
|
+
'push' => Gerrit::Cli::Command::Push.new(logger, runner),
|
23
|
+
}
|
24
|
+
|
25
|
+
@commands['help'] = Gerrit::Cli::Command::Help.new(logger, @commands)
|
26
|
+
end
|
27
|
+
|
28
|
+
def run_command(argv)
|
29
|
+
if argv.empty?
|
30
|
+
@logger.info("Available Commands:")
|
31
|
+
@logger.info(@commands['help'].commands_summary)
|
32
|
+
else
|
33
|
+
args = argv.dup
|
34
|
+
command_name = args.shift
|
35
|
+
if command = @commands[command_name]
|
36
|
+
command.run(args)
|
37
|
+
else
|
38
|
+
@logger.error("ERROR: Unknown command '#{command_name}'")
|
39
|
+
@commands['help'].show_command_summaries
|
40
|
+
end
|
41
|
+
end
|
42
|
+
rescue Gerrit::Cli::UsageError => ue
|
43
|
+
@logger.error("ERROR: #{ue}\n")
|
44
|
+
command.show_usage
|
45
|
+
exit 1
|
46
|
+
rescue => e
|
47
|
+
@logger.error("ERROR: #{e}")
|
48
|
+
@logger.debug(e.backtrace.join("\n")) if e.backtrace
|
49
|
+
exit 1
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
require 'gerrit/cli/errors'
|
4
|
+
|
5
|
+
module Gerrit
|
6
|
+
module Cli
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Gerrit::Cli::ShellRunner
|
11
|
+
def initialize(logger)
|
12
|
+
@logger = logger || Logger.new(STDOUT)
|
13
|
+
end
|
14
|
+
|
15
|
+
def system!(command)
|
16
|
+
@logger.debug("+ #{command}")
|
17
|
+
|
18
|
+
unless system(command)
|
19
|
+
st = $?.exitstatus
|
20
|
+
emsg = "Command '#{command}' exited with non-zero status (#{st})."
|
21
|
+
raise Gerrit::Cli::Error.new(emsg)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def capture!(command)
|
26
|
+
@logger.debug("+ #{command}")
|
27
|
+
|
28
|
+
out = `#{command}`
|
29
|
+
unless $?.success?
|
30
|
+
st = $?.exitstatus
|
31
|
+
emsg = "Command '#{command}' exited with non-zero status (#{st})."
|
32
|
+
raise Gerrit::Cli::Error.new(emsg)
|
33
|
+
end
|
34
|
+
|
35
|
+
out
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Gerrit
|
2
|
+
module Cli
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
6
|
+
module Gerrit::Cli::Util
|
7
|
+
class << self
|
8
|
+
def render_table(rows, opts={})
|
9
|
+
return "" if rows.empty?
|
10
|
+
|
11
|
+
max_col_lengths = []
|
12
|
+
rows.first.length.times { max_col_lengths << 0 }
|
13
|
+
|
14
|
+
# Compute maximum length of each column
|
15
|
+
rows.each do |row|
|
16
|
+
if row.length != max_col_lengths.length
|
17
|
+
raise ArgumentError, "Column mismatch"
|
18
|
+
end
|
19
|
+
|
20
|
+
row.each_with_index do |c, ii|
|
21
|
+
len = c.to_s.length
|
22
|
+
if len> max_col_lengths[ii]
|
23
|
+
max_col_lengths[ii] = len
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
delim = opts[:delimiter] || ' '
|
29
|
+
row_format = max_col_lengths.map {|len| "%-#{len}s" }.join(delim)
|
30
|
+
|
31
|
+
rows.map {|row| row_format % row }.join("\n")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|