gerrit-cli 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+ require "rspec/core/version"
5
+
6
+ desc "Run all specs"
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.rspec_opts = %w[--color --format documentation]
9
+ end
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)
@@ -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,5 @@
1
+ module Gerrit
2
+ module Cli
3
+ DEFAULT_GERRIT_PORT = 29418
4
+ end
5
+ 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,6 @@
1
+ module Gerrit
2
+ module Cli
3
+ class Error < StandardError; end
4
+ class UsageError < Error; end
5
+ end
6
+ 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