gerrit-cli 0.0.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 +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
|