Class: SugarJar::Commands
- Inherits:
-
Object
- Object
- SugarJar::Commands
- Defined in:
- lib/sugarjar/commands.rb,
lib/sugarjar/commands/up.rb,
lib/sugarjar/commands/push.rb,
lib/sugarjar/commands/amend.rb,
lib/sugarjar/commands/bclean.rb,
lib/sugarjar/commands/branch.rb,
lib/sugarjar/commands/checks.rb,
lib/sugarjar/commands/feature.rb,
lib/sugarjar/commands/debuginfo.rb,
lib/sugarjar/commands/smartclone.rb,
lib/sugarjar/commands/pullsuggestions.rb,
lib/sugarjar/commands/smartpullrequest.rb
Overview
This is the workhorse of SugarJar. Short of #initialize, all other public methods are “commands”. Anything in private is internal implementation details.
Constant Summary collapse
- MAIN_BRANCHES =
%w{master main}.freeze
Instance Method Summary collapse
- #amend ⇒ Object
- #bclean(name = nil) ⇒ Object
- #bcleanall ⇒ Object
- #binfo ⇒ Object
- #br ⇒ Object
- #checkout(*args) ⇒ Object (also: #co)
- #debuginfo(*args) ⇒ Object
- #feature(name, base = nil) ⇒ Object (also: #f)
- #forcepush(remote = nil, branch = nil) ⇒ Object (also: #fpush)
-
#get_checks(type) ⇒ Object
determine if we’re using the _list_cmd and if so run it to get the checks, or just use the directly-defined check, and cache it.
- #get_checks_from_command(type) ⇒ Object
-
#initialize(options) ⇒ Commands
constructor
A new instance of Commands.
- #lint ⇒ Object
- #pullsuggestions ⇒ Object (also: #ps)
- #qamend ⇒ Object (also: #amendq)
- #run_check(type) ⇒ Object
- #smartclone(repo, dir = nil) ⇒ Object (also: #sclone)
-
#smartlog ⇒ Object
(also: #sl)
binfo for all branches.
- #smartpullrequest(*args) ⇒ Object (also: #spr, #smartpr)
- #smartpush(remote = nil, branch = nil) ⇒ Object (also: #spush)
- #subfeature(name) ⇒ Object (also: #sf)
- #unit ⇒ Object
- #up(branch = nil) ⇒ Object
- #upall ⇒ Object
Constructor Details
#initialize(options) ⇒ Commands
Returns a new instance of Commands.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/sugarjar/commands.rb', line 26 def initialize() SugarJar::Log.debug("Commands.initialize options: #{}") @ignore_dirty = ['ignore_dirty'] @ignore_prerun_failure = ['ignore_prerun_failure'] @repo_config = SugarJar::RepoConfig.config SugarJar::Log.debug("Repoconfig: #{@repo_config}") @color = ['color'] @pr_autofill = ['pr_autofill'] @pr_autostack = ['pr_autostack'] @feature_prefix = ['feature_prefix'] @checks = {} @main_branch = nil @main_remote_branches = {} @ghuser = @repo_config['github_user'] || ['github_user'] @ghhost = @repo_config['github_host'] || ['github_host'] die("No 'gh' found, please install 'gh'") unless gh_avail? # Tell the 'gh' cli where to talk to, if not github.com ENV['GH_HOST'] = @ghhost if @ghhost return if ['no_change'] set_commit_template if @repo_config['commit_template'] end |
Instance Method Details
#amend ⇒ Object
5 6 7 8 9 |
# File 'lib/sugarjar/commands/amend.rb', line 5 def amend(*) assert_in_repo! # This cannot use shellout since we need a full terminal for the editor exit(system(SugarJar::Util.which('git'), 'commit', '--amend', *)) end |
#bclean(name = nil) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 |
# File 'lib/sugarjar/commands/bclean.rb', line 3 def bclean(name = nil) assert_in_repo! name ||= current_branch name = fprefix(name) if clean_branch(name) SugarJar::Log.info("#{name}: #{color('reaped', :green)}") else die( "#{color("Cannot clean #{name}", :red)}! there are unmerged " + "commits; use 'git branch -D #{name}' to forcefully delete it.", ) end end |
#bcleanall ⇒ Object
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/sugarjar/commands/bclean.rb', line 17 def bcleanall assert_in_repo! curr = current_branch all_local_branches.each do |branch| if MAIN_BRANCHES.include?(branch) SugarJar::Log.debug("Skipping #{branch}") next end if clean_branch(branch) SugarJar::Log.info("#{branch}: #{color('reaped', :green)}") else SugarJar::Log.info("#{branch}: skipped") SugarJar::Log.debug( "There are unmerged commits; use 'git branch -D #{branch}' to " + 'forcefully delete it)', ) end end # Return to the branch we were on, or main if all_local_branches.include?(curr) git('checkout', curr) else checkout_main_branch end end |
#binfo ⇒ Object
24 25 26 27 28 29 30 |
# File 'lib/sugarjar/commands/branch.rb', line 24 def binfo assert_in_repo! SugarJar::Log.info(git( 'log', '--graph', '--oneline', '--decorate', '--boundary', "#{tracked_branch}.." ).stdout.chomp) end |
#br ⇒ Object
19 20 21 22 |
# File 'lib/sugarjar/commands/branch.rb', line 19 def br assert_in_repo! SugarJar::Log.info(git('branch', '-v').stdout.chomp) end |
#checkout(*args) ⇒ Object Also known as: co
3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# File 'lib/sugarjar/commands/branch.rb', line 3 def checkout(*args) assert_in_repo! # Pop the last arguement, which is _probably_ a branch name # and then add any featureprefix, and if _that_ is a branch # name, replace the last arguement with that name = args.last bname = fprefix(name) if all_local_branches.include?(bname) SugarJar::Log.debug("Featurepefixing #{name} -> #{bname}") args[-1] = bname end s = git('checkout', *args) SugarJar::Log.info(s.stderr + s.stdout.chomp) end |
#debuginfo(*args) ⇒ Object
5 6 7 8 9 10 11 12 13 14 |
# File 'lib/sugarjar/commands/debuginfo.rb', line 5 def debuginfo(*args) puts "sugarjar version #{SugarJar::VERSION}" puts ghcli('version').stdout puts git('version').stdout puts "Config: #{JSON.pretty_generate(args[0])}" return unless @repo_config puts "Repo config: #{JSON.pretty_generate(@repo_config)}" end |
#feature(name, base = nil) ⇒ Object Also known as: f
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/sugarjar/commands/feature.rb', line 3 def feature(name, base = nil) assert_in_repo! SugarJar::Log.debug("Feature: #{name}, #{base}") name = fprefix(name) die("#{name} already exists!") if all_local_branches.include?(name) if base fbase = fprefix(base) base = fbase if all_local_branches.include?(fbase) else base ||= most_main end # If our base is a local branch, don't try to parse it for a remote name unless all_local_branches.include?(base) base_pieces = base.split('/') git('fetch', base_pieces[0]) if base_pieces.length > 1 end git('checkout', '-b', name, base) git('branch', '-u', base) SugarJar::Log.info( "Created feature branch #{color(name, :green)} based on " + color(base, :green), ) end |
#forcepush(remote = nil, branch = nil) ⇒ Object Also known as: fpush
9 10 11 12 |
# File 'lib/sugarjar/commands/push.rb', line 9 def forcepush(remote = nil, branch = nil) assert_in_repo! _smartpush(remote, branch, true) end |
#get_checks(type) ⇒ Object
determine if we’re using the _list_cmd and if so run it to get the checks, or just use the directly-defined check, and cache it
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/sugarjar/commands/checks.rb', line 54 def get_checks(type) return @checks[type] if @checks[type] ret = get_checks_from_command(type) if ret SugarJar::Log.debug("Found #{type}s: #{ret}") @checks[type] = ret # if it's explicitly false, we failed to run the command elsif ret == false @checks[type] = false # otherwise, we move on (basically: it's nil, there was no _list_cmd) else SugarJar::Log.debug("[#{type}]: using listed linters: #{ret}") @checks[type] = @repo_config[type] || [] end @checks[type] end |
#get_checks_from_command(type) ⇒ Object
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/sugarjar/commands/checks.rb', line 31 def get_checks_from_command(type) return nil unless @repo_config["#{type}_list_cmd"] cmd = @repo_config["#{type}_list_cmd"] short = cmd.split.first unless File.exist?(short) SugarJar::Log.error( "Configured #{type}_list_cmd #{short} does not exist!", ) return false end s = Mixlib::ShellOut.new(cmd).run_command if s.error? SugarJar::Log.error( "#{type}_list_cmd (#{cmd}) failed: #{s.format_for_exception}", ) return false end s.stdout.split("\n") end |
#lint ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/sugarjar/commands/checks.rb', line 5 def lint assert_in_repo! if dirty? if @ignore_dirty SugarJar::Log.warn( 'Your repo is dirty, but --ignore-dirty was specified, so ' + 'carrying on anyway. If the linter autocorrects, the displayed ' + 'diff will be misleading', ) else SugarJar::Log.error( 'Your repo is dirty, but --ignore-dirty was not specified. ' + 'Refusing to run lint. This is to ensure that if the linter ' + 'autocorrects, we can show the correct diff.', ) exit(1) end end exit(1) unless run_check('lint') end |
#pullsuggestions ⇒ Object Also known as: ps
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/sugarjar/commands/pullsuggestions.rb', line 5 def pullsuggestions assert_in_repo! if dirty? if @ignore_dirty SugarJar::Log.warn( 'Your repo is dirty, but --ignore-dirty was specified, so ' + 'carrying on anyway.', ) else SugarJar::Log.error( 'Your repo is dirty, so I am not going to push. Please commit ' + 'or amend first.', ) exit(1) end end src = "origin/#{current_branch}" fetch('origin') diff = git('diff', "..#{src}").stdout return unless diff && !diff.empty? puts "Will merge the following suggestions:\n\n#{diff}" loop do $stdout.print("\nAre you sure? [y/n] ") ans = $stdin.gets.strip case ans when /^[Yy]$/ git = SugarJar::Util.which('git') system(git, 'merge', '--ff', "origin/#{current_branch}") break when /^[Nn]$/, /^[Qq](uit)?/ puts 'Not merging at user request...' break else puts "Didn't understand '#{ans}'." end end end |
#qamend ⇒ Object Also known as: amendq
11 12 13 14 |
# File 'lib/sugarjar/commands/amend.rb', line 11 def qamend(*) assert_in_repo! SugarJar::Log.info(git('commit', '--amend', '--no-edit', *).stdout) end |
#run_check(type) ⇒ Object
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/sugarjar/commands/checks.rb', line 72 def run_check(type) repo_root = SugarJar::Util.repo_root Dir.chdir repo_root do checks = get_checks(type) # if we failed to determine the checks, the the checks have effectively # failed return false unless checks checks.each do |check| SugarJar::Log.debug("Running #{type} #{check}") short = check.split.first if short.include?('/') short = File.join(repo_root, short) unless short.start_with?('/') unless File.exist?(short) SugarJar::Log.error("Configured #{type} #{short} does not exist!") end elsif !SugarJar::Util.which_nofail(short) SugarJar::Log.error("Configured #{type} #{short} does not exist!") return false end s = Mixlib::ShellOut.new(check).run_command # Linters auto-correct, lets handle that gracefully if type == 'lint' && dirty? SugarJar::Log.info( "[#{type}] #{short}: #{color('Corrected', :yellow)}", ) SugarJar::Log.warn( "The linter modified the repo. Here's the diff:\n", ) puts git('diff').stdout loop do $stdout.print( "\nWould you like to\n\t[q]uit and inspect\n\t[a]mend the " + "changes to the current commit and re-run\n > ", ) ans = $stdin.gets.strip case ans when /^q/ SugarJar::Log.info('Exiting at user request.') exit(1) when /^a/ qamend('-a') # break here, if we get out of this loop we 'redo', assuming # the user chose this option break end end redo end if s.error? SugarJar::Log.info( "[#{type}] #{short} #{color('failed', :red)}, output follows " + "(see debug for more)\n#{s.stdout}", ) SugarJar::Log.debug(s.format_for_exception) return false end SugarJar::Log.info( "[#{type}] #{short}: #{color('OK', :green)}", ) end end end |
#smartclone(repo, dir = nil) ⇒ Object Also known as: sclone
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/sugarjar/commands/smartclone.rb', line 3 def smartclone(repo, dir = nil, *) reponame = File.basename(repo, '.git') dir ||= reponame org = extract_org(repo) SugarJar::Log.info("Cloning #{reponame}...") # GH's 'fork' command (with the --clone arg) will fork, if necessary, # then clone, and then setup the remotes with the appropriate names. So # we just let it do all the work for us and return. # # Unless the repo is in our own org and cannot be forked, then it # will fail. if org == @ghuser git('clone', canonicalize_repo(repo), dir, *) else ghcli('repo', 'fork', '--clone', canonicalize_repo(repo), dir, *) # make the main branch track upstream Dir.chdir dir do git('branch', '-u', "upstream/#{main_branch}") end end SugarJar::Log.info('Remotes "origin" and "upstream" configured.') end |
#smartlog ⇒ Object Also known as: sl
binfo for all branches
33 34 35 36 37 38 39 |
# File 'lib/sugarjar/commands/branch.rb', line 33 def smartlog assert_in_repo! SugarJar::Log.info(git( 'log', '--graph', '--oneline', '--decorate', '--boundary', '--branches', "#{most_main}.." ).stdout.chomp) end |
#smartpullrequest(*args) ⇒ Object Also known as: spr, smartpr
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/sugarjar/commands/smartpullrequest.rb', line 5 def smartpullrequest(*args) assert_in_repo! assert_common_main_branch! if dirty? SugarJar::Log.warn( 'Your repo is dirty, so I am not going to create a pull request. ' + 'You should commit or amend and push it to your remote first.', ) exit(1) end user_specified_base = args.include?('-B') || args.include?('--base') curr = current_branch base = tracked_branch if @pr_autofill SugarJar::Log.info('Autofilling in PR from commit message') num_commits = git( 'rev-list', '--count', curr, "^#{base}" ).stdout.strip.to_i if num_commits > 1 args.unshift('--fill-first') else args.unshift('--fill') end end unless user_specified_base if subfeature?(base) if upstream_org != push_org SugarJar::Log.warn( 'Unfortunately you cannot based one PR on another PR when' + " using fork-based PRs. We will base this on #{most_main}." + ' This just means the PR "Changes" tab will show changes for' + ' the full stack until those other PRs are merged and this PR' + ' PR is rebased.', ) # nil is prompt, true is always, false is never elsif @pr_autostack.nil? $stdout.print( 'It looks like this is a subfeature, would you like to base ' + "this PR on #{base}? [y/n] ", ) ans = $stdin.gets.strip args.unshift('--base', base) if %w{Y y}.include?(ans) elsif @pr_autostack args.unshift('--base', base) end elsif base.include?('/') && base != most_main # If our base is a remote branch, then use that as the # base branch of the PR args.unshift('--base', base.split('/').last) end end # <org>:<branch> is the GH API syntax for: # look for a branch of name <branch>, from a fork in owner <org> args.unshift('--head', "#{push_org}:#{curr}") SugarJar::Log.trace("Running: gh pr create #{args.join(' ')}") gh = SugarJar::Util.which('gh') system(gh, 'pr', 'create', *args) end |
#smartpush(remote = nil, branch = nil) ⇒ Object Also known as: spush
3 4 5 6 |
# File 'lib/sugarjar/commands/push.rb', line 3 def smartpush(remote = nil, branch = nil) assert_in_repo! _smartpush(remote, branch, false) end |
#subfeature(name) ⇒ Object Also known as: sf
28 29 30 31 32 |
# File 'lib/sugarjar/commands/feature.rb', line 28 def subfeature(name) assert_in_repo! SugarJar::Log.debug("Subfature: #{name}") feature(name, current_branch) end |
#unit ⇒ Object
26 27 28 29 |
# File 'lib/sugarjar/commands/checks.rb', line 26 def unit assert_in_repo! exit(1) unless run_check('unit') end |
#up(branch = nil) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/sugarjar/commands/up.rb', line 3 def up(branch = nil) assert_in_repo! branch ||= current_branch branch = fprefix(branch) # get a copy of our current branch, if rebase fails, we won't # be able to determine it without backing out curr = current_branch git('checkout', branch) result = rebase if result['so'].error? backout = '' if rebase_in_progress? backout = ' You can get out of this with a `git rebase --abort`.' end die( "#{color(curr, :red)}: Failed to rebase on " + "#{result['base']}. Leaving the repo as-is.#{backout} " + 'Output from failed rebase is: ' + "\nSTDOUT:\n#{result['so'].stdout.lines.map { |x| "\t#{x}" }.join}" + "\nSTDERR:\n#{result['so'].stderr.lines.map { |x| "\t#{x}" }.join}", ) else SugarJar::Log.info( "#{color(current_branch, :green)} rebased on #{result['base']}", ) # go back to where we were if we rebased a different branch git('checkout', curr) if branch != curr end end |
#upall ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/sugarjar/commands/up.rb', line 34 def upall assert_in_repo! all_local_branches.each do |branch| next if MAIN_BRANCHES.include?(branch) git('checkout', branch) result = rebase if result['so'].error? SugarJar::Log.error( "#{color(branch, :red)} failed rebase. Reverting attempt and " + 'moving to next branch. Try `sj up` manually on that branch.', ) git('rebase', '--abort') if rebase_in_progress? else SugarJar::Log.info( "#{color(branch, :green)} rebased on " + color(result['base'], :green).to_s, ) end end end |