Top Level Namespace
Defined Under Namespace
Constant Summary collapse
- GERGICH_REVIEW_LABEL =
ENV.fetch("GERGICH_REVIEW_LABEL", "Code-Review")
- GERGICH_USER =
ENV.fetch("GERGICH_USER", "gergich")
- GERGICH_GIT_PATH =
ENV.fetch("GERGICH_GIT_PATH", ".")
- GergichError =
Class.new(StandardError)
- CI_TEST_ARGS =
{ "comment" => [ [ { path: "foo.rb", position: 3, severity: "error", message: "ಠ_ಠ" }, { path: "/COMMIT_MSG", position: 1, severity: "info", message: "cool story bro" }, { path: "/COMMIT_MSG", severity: "info", message: "lol", position: { start_line: 1, start_character: 1, end_line: 1, end_character: 2 } }, { path: "/COMMIT_MSG", severity: "info", message: "more", position: { start_line: 1, start_character: 5, end_line: 1, end_character: 5 } } ].to_json ], "label" => ["Code-Review", 1], "message" => ["this is a test"], "capture" => ["rubocop", format("echo %<output>s", output: Shellwords.escape(<<~OUTPUT))] bin/gergich:47:8: C: Prefer double-quoted strings if ENV['DEBUG'] ^^^^^^^ 1 file inspected, 35 offenses detected, 27 offenses auto-correctable OUTPUT }.freeze
- MASTER_BOUNCER_REVIEW_LABEL =
ENV.fetch("GERGICH_REVIEW_LABEL", "Code-Review")
- PROJECT =
ENV["GERRIT_PROJECT"] || error("no GERRIT_PROJECT set")
- MAIN_BRANCH =
on Jenkins GERRIT_MAIN_BRANCH env var will default to empty string if not set
ENV["GERRIT_MAIN_BRANCH"]
- WARN_DISTANCE =
TODO: time-based thresholds
ENV.fetch("MASTER_BOUNCER_WARN_DISTANCE", 50).to_i
- ERROR_DISTANCE =
ENV.fetch("MASTER_BOUNCER_ERROR_DISTANCE", 100).to_i
Instance Method Summary collapse
- #error(text) ⇒ Object
- #help_command(commands) ⇒ Object
- #info(text) ⇒ Object
- #maybe_bounce_commit!(commit) ⇒ Object
- #potentially_mergeable_changes ⇒ Object
- #run_app(commands) ⇒ Object
- #run_ci_test!(all_commands) ⇒ Object
- #run_command(action) ⇒ Object
- #script_name ⇒ Object
- #usage(content = nil) ⇒ Object
Instance Method Details
#error(text) ⇒ Object
24 25 26 27 28 |
# File 'lib/gergich/cli.rb', line 24 def error(text) $stderr.puts "\e[31mError:\e[0m #{text}" $stderr.puts usage exit 1 end |
#help_command(commands) ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/gergich/cli.rb', line 30 def help_command(commands) { action: ->(subcommand = "help") { subcommand_info = commands[subcommand] if !subcommand_info error "Unrecognized command `#{subcommand}`" elsif (help_text = subcommand_info[:help]) info help_text.respond_to?(:call) ? help_text.call : help_text else error "No help available for `#{subcommand}`" end }, help: -> { indentation = commands.keys.map(&:size).max commands_help = commands .to_a .sort_by(&:first) .map { |key, data| "#{key.ljust(indentation)} - #{data[:summary]}" if data[:summary] } .compact usage(commands_help.join("\n")) } } end |
#info(text) ⇒ Object
9 10 11 12 |
# File 'lib/gergich/cli.rb', line 9 def info(text) puts text exit end |
#maybe_bounce_commit!(commit) ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/gergich/cli/master_bouncer.rb', line 29 def maybe_bounce_commit!(commit) draft = Gergich::Draft.new(commit) draft.reset! distance = Gergich.git("rev-list origin/#{MAIN_BRANCH} ^#{commit.ref} --count").to_i detail = "#{distance} commits behind #{MAIN_BRANCH}" score = 0 = nil if distance > ERROR_DISTANCE score = -2 = "This commit is probably not safe to merge (#{detail}). You'll " \ "need to rebase it to ensure all the tests still pass." elsif distance > WARN_DISTANCE score = -1 = "This commit may not be safe to merge (#{detail}). Please " \ "rebase to make sure all the tests still pass." end review = Gergich::Review.new(commit, draft) current_score = review.current_score puts "#{detail}, " + (score == current_score ? "score still #{score}" : "changing score from #{current_score} to #{score}") # since we run on a daily cron, we might be checking the same patchset # many times, so bail if nothing has changed return if score == current_score draft.add_label MASTER_BOUNCER_REVIEW_LABEL, score draft. if # otherwise we always publish ... even in the score=0 case it's # important, as we might be undoing a previous negative score. # similarly, over time the same patchset will become more out of date, # so we allow_repost (so to speak) so we can add increasingly negative # reviews review.publish!(allow_repost: true) end |
#potentially_mergeable_changes ⇒ Object
19 20 21 22 23 24 25 26 27 |
# File 'lib/gergich/cli/master_bouncer.rb', line 19 def potentially_mergeable_changes url = "/changes/?q=status:open+" \ "p:#{PROJECT}+" \ "label:Verified=1+" \ "branch:#{MAIN_BRANCH}" \ "&o=CURRENT_REVISION" changes = Gergich::API.get(url) changes.reject { |c| c["subject"] =~ /\Awip($|\W)/i } end |
#run_app(commands) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/gergich/cli.rb', line 68 def run_app(commands) commands["help"] = help_command(commands) command = ARGV.shift || "help" if commands[command] begin action = commands[command][:action] run_command(action) rescue GergichError error $ERROR_INFO. rescue StandardError error "Unhandled exception: #{$ERROR_INFO}\n#{$ERROR_INFO.backtrace.join("\n")}" end else error "Unrecognized command `#{command}`" end end |
#run_ci_test!(all_commands) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/gergich/cli/gergich.rb', line 27 def run_ci_test!(all_commands) ENV["GERGICH_CHANGE_NAME"] = "0" commands_to_test = all_commands - %w[citest reset publish status] commands_to_test << "status" # put it at the end, so we maximize the stuff it tests commands = commands_to_test.map { |command| [command, CI_TEST_ARGS[command] || []] } commands.concat(all_commands.map { |command| ["help", [command]] }) # after running our test commands, reset and publish frd: commands << ["reset"] # Note that while gergich should always be able to vote on these labels, he may be used to # vone on other branches depending on project usage and project-specific permissions commands << ["label", ["Lint-Review", 1]] commands << ["label", ["Code-Review", 1]] # Not all projects have Lint-Review commands << ["message", ["\`gergich citest\` checks out :thumbsup: :mj:"]] commands << ["publish"] commands.each do |command, args = []| arglist = args.map { |arg| Shellwords.escape(arg.to_s) } output = `bundle exec gergich #{command} #{arglist.join(" ")} 2>&1` unless $CHILD_STATUS.success? error "`gergich citest` failed on step `#{command}`:\n\n#{output.gsub(/^/, ' ')}\n" end end end |
#run_command(action) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/gergich/cli.rb', line 56 def run_command(action) params = action.parameters params.each_with_index do |(type, name), i| error "No <#{name}> specified" if i >= ARGV.size && type == :req end if ARGV.size > params.size extra_args = ARGV[params.size, ARGV.size].map { |a| "`#{Shellwords.escape(a)}`" } error "Extra arg(s) #{extra_args.join(' ')}" end action.call(*ARGV) end |
#script_name ⇒ Object
14 15 16 |
# File 'lib/gergich/cli.rb', line 14 def script_name $PROGRAM_NAME.sub(%r{.*/}, "") end |
#usage(content = nil) ⇒ Object
18 19 20 21 22 |
# File 'lib/gergich/cli.rb', line 18 def usage(content = nil) "Usage: #{script_name} <command> [<args>...]\n" + (content ? "\n#{content}\n\n" : "") + "Tip: run `#{script_name} help <command>` for more info" end |