sugarjar 1.1.3 → 2.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/bin/sj CHANGED
@@ -13,7 +13,8 @@ SugarJar::Log.level = Logger::INFO
13
13
 
14
14
  # Don't put defaults here, put them in SugarJar::Config - otherwise
15
15
  # these defaults overwrite whatever is in config files.
16
- options = { 'color' => true }
16
+ options = {}
17
+
17
18
  # If ENV['SUGARJAR_DEBUG'] is set, it overrides the config file,
18
19
  # but not the command line options, so set that one here. Also
19
20
  # start the logger at that level, in case we are debugging option loading
@@ -21,6 +22,7 @@ options = { 'color' => true }
21
22
  if ENV['SUGARJAR_LOGLEVEL']
22
23
  options['log_level'] = SugarJar::Log.level = ENV['SUGARJAR_LOGLEVEL'].to_sym
23
24
  end
25
+
24
26
  parser = OptionParser.new do |opts|
25
27
  opts.banner = 'Usage: sj <command> [<args>] [<options>]'
26
28
 
@@ -29,24 +31,10 @@ parser = OptionParser.new do |opts|
29
31
  opts.separator ''
30
32
  opts.separator 'OPTIONS:'
31
33
 
32
- opts.on('--[no-]fallthru', 'Fall-thru to git. [default: true]') do |fallthru|
33
- options['fallthru'] = fallthru
34
- end
35
-
36
34
  opts.on('--feature-prefix', 'Prefix to use for feature branches') do |prefix|
37
35
  options['feature_prefix'] = prefix
38
36
  end
39
37
 
40
- opts.on(
41
- '--github-cli CLI',
42
- %w{gh cli},
43
- 'Github CLI to use ("gh" or "hub" or "auto"). Auto (the default) will ' +
44
- 'prefer "gh" if it is available but will fall back to "hub." ' +
45
- '[default: "auto"]',
46
- ) do |cli|
47
- options['github_cli'] = cli
48
- end
49
-
50
38
  opts.on(
51
39
  '--github-host HOST',
52
40
  'The host for "hub". Note that we will set this in the local repo ' +
@@ -109,7 +97,7 @@ parser = OptionParser.new do |opts|
109
97
  options['pr_autostack'] = autostack
110
98
  end
111
99
 
112
- opts.on('--[no-]use-color', 'Enable color. [default: true]') do |color|
100
+ opts.on('--[no-]color', 'Enable color. [default: true]') do |color|
113
101
  options['color'] = color
114
102
  end
115
103
 
@@ -119,7 +107,7 @@ parser = OptionParser.new do |opts|
119
107
  end
120
108
 
121
109
  # rubocop:disable Layout/HeredocIndentation
122
- opts.separator <<COMMANDS
110
+ opts.separator <<COMMANDTEXT
123
111
 
124
112
  COMMANDS:
125
113
  amend
@@ -145,6 +133,10 @@ COMMANDS:
145
133
  br
146
134
  Verbose branch list. An alias for "git branch -v".
147
135
 
136
+ debuginfo
137
+ Prints out a bunch of version and config information useful for
138
+ including in bug reports.
139
+
148
140
  feature, f <branch_name>
149
141
  Create a "feature" branch. It's morally equivalent to
150
142
  "git checkout -b" except it defaults to creating it based on
@@ -209,99 +201,66 @@ COMMANDS:
209
201
 
210
202
  upall
211
203
  Same as "up", but for all branches.
212
-
213
- version
214
- Print the version of sugarjar, and then run 'hub version'
215
- to show the hub and git versions.
216
-
217
- Be sure to checkout Sapling (https://sapling-scm.com/)! SugarJar was written as
218
- a stop-gap to get Sapling features before it was open-sourced, and as such
219
- Sapling may serve your needs even better.
220
- COMMANDS
204
+ COMMANDTEXT
221
205
 
222
206
  # rubocop:enable Layout/HeredocIndentation
223
207
  end
224
208
 
225
- # we make a copy of these because we will assign back to the ARGV
226
- # we parse later. We also need a pristine copy in case we want to
227
- # run git as we were called.
228
- argv_copy = ARGV.dup
229
-
230
- # We don't have options yet, but we need an instance of SJ in order
231
- # to list public methods. We will recreate it
232
- sj = SugarJar::Commands.new(options.merge({ 'no_change' => true }))
233
209
  extra_opts = []
210
+ argv_copy = ARGV.dup
234
211
 
235
- # as with above, this can't go into 'options', until after we parse
236
- # the command line args
237
- config = SugarJar::Config.config
238
-
239
- valid_commands = sj.public_methods - Object.public_methods
240
- possible_valid_command = ARGV.any? do |arg|
241
- valid_commands.include?(arg.to_s.to_sym)
242
- end
243
-
244
- # if we're configured to fall thru and the subcommand isn't one
245
- # we recognize, don't parse the options as they may be different
246
- # than git's. For example `git config -l` - we error because we
247
- # require an arguement to `-l`.
248
- if config['fallthru'] && !possible_valid_command
249
- SugarJar::Log.debug(
250
- 'Skipping option parsing: fall-thru is set and we do not recognize ' +
251
- 'any subcommands',
252
- )
253
- else
212
+ # We want to allow people to pass in extra args to be passed to commands (like
213
+ # `amend`), but OptionParser doesn't easily allow this. So we loop over it,
214
+ # catching exceptions.
215
+
216
+ begin
217
+ # HOWEVER, anytime it throws an exception, for some reason, it clears
218
+ # out all of ARGV, or whatever you passed to as ARGV.
219
+ #
220
+ # This not only prevents further parsing, but also means we lose
221
+ # any non-option arguements (like the subcommand!)
222
+ #
223
+ # So we save a copy, and if we throw an exception, save the option that
224
+ # caused it, remove that option from our copy, and then re-populate argv
225
+ # with what's left.
226
+ #
227
+ # By doing this we not only get to parse all the options properly and
228
+ # save unknown ones, but non-option arguements, which OptionParser
229
+ # normally leaves in ARGV stay in ARGV.
230
+ saved_argv = argv_copy.dup
231
+ parser.parse!(argv_copy)
232
+ rescue OptionParser::InvalidOption => e
233
+ SugarJar::Log.debug("Saving unknown argument #{e.args}")
234
+ extra_opts += e.args
235
+
236
+ # e.args is an array, but it's only ever one arguement per exception
237
+ saved_argv.delete(e.args.first)
238
+ argv_copy = saved_argv.dup
254
239
  SugarJar::Log.debug(
255
- 'We MIGHT have a valid command... parse-command line options',
240
+ "Continuing option parsing with remaining ARGV: #{argv_copy}",
256
241
  )
257
- # We want to allow people to pass in extra args to be passed to
258
- # git commands, but OptionParser doesn't easily allow this. So we
259
- # loop over it, catching exceptions.
260
- begin
261
- # HOWEVER, anytime it throws an exception, for some reason, it clears
262
- # out all of ARGV, or whatever you passed to as ARGV.
263
- #
264
- # This not only prevents further parsing, but also means we lose
265
- # any non-option arguements (like the subcommand!)
266
- #
267
- # So we save a copy, and if we throw an exception, save the option that
268
- # caused it, remove that option from our copy, and then re-populate argv
269
- # with what's left.
270
- #
271
- # By doing this we not only get to parse all the options properly and
272
- # save unknown ones, but non-option arguements, which OptionParser
273
- # normally leaves in ARGV stay in ARGV.
274
- saved_argv = argv_copy.dup
275
- parser.parse!(argv_copy)
276
- rescue OptionParser::InvalidOption => e
277
- SugarJar::Log.debug("Saving unknown argument #{e.args}")
278
- extra_opts += e.args
279
-
280
- # e.args is an array, but it's only ever one arguement per exception
281
- saved_argv.delete(e.args.first)
282
- argv_copy = saved_argv.dup
283
- SugarJar::Log.debug(
284
- "Continuing option parsing with remaining ARGV: #{argv_copy}",
285
- )
286
- retry
287
- end
242
+ retry
288
243
  end
289
244
 
290
- subcommand = argv_copy.reject { |x| x.start_with?('-') }.first
245
+ options = SugarJar::Config.config.merge(options)
246
+ SugarJar::Log.level = options['log_level'].to_sym if options['log_level']
291
247
 
248
+ subcommand = argv_copy.reject { |x| x.start_with?('-') }.first
292
249
  if ARGV.empty? || !subcommand
293
250
  puts parser
294
251
  exit
295
252
  end
296
253
 
297
- options = config.merge(options)
254
+ SugarJar::Log.debug("Final config: #{options}")
298
255
 
299
- # Recreate SJ with all of our options
300
- SugarJar::Log.level = options['log_level'].to_sym if options['log_level']
301
256
  sj = SugarJar::Commands.new(options)
302
-
257
+ valid_commands = sj.public_methods - Object.public_methods
303
258
  is_valid_command = valid_commands.include?(subcommand.to_sym)
304
- argv_copy.delete(subcommand)
259
+ # We can't do .delete(subcommand) because someone could, for example
260
+ # have a branch called 'co' and then do 'sj co co' - which will then
261
+ # remove _all_ instances of 'co'. So find the first instance and remove
262
+ # that.
263
+ argv_copy.delete_at(argv_copy.find_index(subcommand))
305
264
  SugarJar::Log.debug("subcommand is #{subcommand}")
306
265
 
307
266
  # Extra options we got, plus any left over arguements are what we
@@ -309,25 +268,20 @@ SugarJar::Log.debug("subcommand is #{subcommand}")
309
268
  extra_opts += argv_copy
310
269
  SugarJar::Log.debug("extra unknown options: #{extra_opts}")
311
270
 
312
- if subcommand == 'help'
271
+ case subcommand
272
+ when 'help'
313
273
  puts parser
314
274
  exit
275
+ when 'debuginfo'
276
+ extra_opts = [options]
315
277
  end
316
278
 
317
- if is_valid_command
318
- SugarJar::Log.debug(
319
- "running #{subcommand}; extra opts: #{extra_opts.join(', ')}",
320
- )
321
- sj.send(subcommand.to_sym, *extra_opts)
322
- elsif options['fallthru']
323
- SugarJar::Log.debug("Falling thru to: hub #{ARGV.join(' ')}")
324
- if options['github_cli'] == 'hub'
325
- exec('hub', *ARGV)
326
- else
327
- # If we're using 'gh', it doesn't have 'git fall thru' support, so
328
- # we pass thru directly to 'git'
329
- exec('git', *ARGV)
330
- end
331
- else
332
- SugarJar::Log.error("No such subcommand: #{subcommand}")
279
+ unless is_valid_command
280
+ SugarJar::Log.fatal("No such subcommand: #{subcommand}")
281
+ exit 1
333
282
  end
283
+
284
+ SugarJar::Log.debug(
285
+ "running #{subcommand}; extra opts: #{extra_opts.join(', ')}",
286
+ )
287
+ sj.send(subcommand.to_sym, *extra_opts)
@@ -0,0 +1,24 @@
1
+ # This is a sample SugarJar config
2
+ #
3
+ # SugarJar will look for this config in:
4
+ #
5
+ # - /etc/sugarjar/config.yaml
6
+ # - ~/.config/sugarjar/config.yaml
7
+ #
8
+ # The latter will overwrite anything in the former.
9
+ #
10
+
11
+ # NOTE: This file does NOT document ALL options since any command-line option
12
+ # to SugarJar is a valid configuration in this file, so see `sj help` for full
13
+ # details.
14
+
15
+ # Autofill in my PRs from my commit message (default: true)
16
+ pr_autofile: true
17
+
18
+ # Auto stack PRs when subfeatures are detected (default is `nil`, which prompts,
19
+ # but use `true` or `false` to force an option without prompting)
20
+ pr_autostack: true
21
+
22
+ # Don't warn about deprecated config file options if they are in this
23
+ # list
24
+ ignore_deprecated_options: [ 'gh_cli' ]
@@ -0,0 +1,77 @@
1
+ # This is a sample `repoconfig` for SugarJar
2
+ #
3
+ # Configs should be named `.sugarjar.yaml` and placed in the root
4
+ # of your repository.
5
+ #
6
+
7
+ # `include_from` is a meta config wich will read from an additional
8
+ # configuration file and merge anything from the file onto whatever is in the
9
+ # primary file. This is helpful to have a repo configuration that applies to
10
+ # all/most developers, but allow individual developers to add to over overwrite
11
+ # specific configurations for themselves. If the file does not exist, this
12
+ # configuration is ignored.
13
+
14
+ include_from: .sugarjar_local.yaml
15
+
16
+ # `overwrite_from` is a meta config which works much like `include_from`,
17
+ # except that if the file is found, everything else in this configuration file
18
+ # will be ignored and the configuration will be entirely read from the
19
+ # referenced file. If the file does not exist, this configuration is ignored.
20
+
21
+ overwrite_from: .sugarjar_local_overwrite.yaml
22
+
23
+ # `lint` is a list of scripts to run when `sj lint` is executed (or, if
24
+ # configured, to run on `sj spush`/`sj fpush` - see `on_push` below).
25
+ # Regardless of where `sj` is run from, these scripts will be run from the root
26
+ # of the repo. If a slash is detected in the first 'word' of the command, it
27
+ # is assumed it is a relative path and `sj` will check that the file exists.
28
+
29
+ lint:
30
+ - scripts/run_rubocop.sh
31
+ - scripts/run_mdl.sh
32
+
33
+ # `unit` is a list of scripts to run when `sj unit` is executed (or, if
34
+ # configured to run on `sj spush`/`sj fpush`- see `on_push` below). Regardless
35
+ # of where `sj` is run from, these scripts will be run from the root of the
36
+ # repo. If a slash is detected in the first 'word' of the command, it is
37
+ # assumed it is a relative path and `sj` will check that the file exists.
38
+
39
+ unit:
40
+ - bundle exec rspec
41
+ - scripts/run_tests.sh
42
+
43
+ # `lint_list_cmd` is like `lint`, except it's a command to run which will
44
+ # determine the proper lints to run and return them, one per line. This is
45
+ # useful, for example, when you want to only run lints relevant to the changed
46
+ # files.
47
+
48
+ lint_list_cmd: scripts/determine_linters.sh
49
+
50
+ # `unit_list_cmd` is like `unit`, except it's a command to run which will
51
+ # determine the proper units to run and return them, one per line. This is
52
+ # useful, for example, when you want to only run tests relevant to the changed
53
+ # files.
54
+
55
+ unit_list_cmd: scripts/determine_tests.sh
56
+
57
+ # `on_push` determines what checks should be run when pushing a repo. Valid
58
+ # options are `lint` and/or `unit` (or nothing, of course).
59
+
60
+ on_push: [lint] # or [lint, unit]
61
+
62
+ # `commit_template` points to a file to set the git `commit.template` config
63
+ # to. This is really useful for ensuring that everyone has the same
64
+ # template configured.
65
+
66
+ commit_template: .git_commit_template.txt
67
+
68
+ # `github_user` is the user to use when talking to GitHub. Overrides any such
69
+ # setting in the regular SugarJar config. Most useful when in the
70
+ # `include_from` file.
71
+
72
+ github_user: myuser
73
+
74
+ # `github_host` is the GitHub host to use when talking to GitHub (for hosted
75
+ # GHE). See `github_user`.
76
+
77
+ github_host: github.sample.com
@@ -0,0 +1,17 @@
1
+ require_relative '../util'
2
+
3
+ class SugarJar
4
+ class Commands
5
+ def amend(*)
6
+ assert_in_repo!
7
+ # This cannot use shellout since we need a full terminal for the editor
8
+ exit(system(SugarJar::Util.which('git'), 'commit', '--amend', *))
9
+ end
10
+
11
+ def qamend(*)
12
+ assert_in_repo!
13
+ SugarJar::Log.info(git('commit', '--amend', '--no-edit', *).stdout)
14
+ end
15
+ alias amendq qamend
16
+ end
17
+ end
@@ -0,0 +1,118 @@
1
+ class SugarJar
2
+ class Commands
3
+ def bclean(name = nil)
4
+ assert_in_repo!
5
+ name ||= current_branch
6
+ name = fprefix(name)
7
+ if clean_branch(name)
8
+ SugarJar::Log.info("#{name}: #{color('reaped', :green)}")
9
+ else
10
+ die(
11
+ "#{color("Cannot clean #{name}", :red)}! there are unmerged " +
12
+ "commits; use 'git branch -D #{name}' to forcefully delete it.",
13
+ )
14
+ end
15
+ end
16
+
17
+ def bcleanall
18
+ assert_in_repo!
19
+ curr = current_branch
20
+ all_local_branches.each do |branch|
21
+ if MAIN_BRANCHES.include?(branch)
22
+ SugarJar::Log.debug("Skipping #{branch}")
23
+ next
24
+ end
25
+
26
+ if clean_branch(branch)
27
+ SugarJar::Log.info("#{branch}: #{color('reaped', :green)}")
28
+ else
29
+ SugarJar::Log.info("#{branch}: skipped")
30
+ SugarJar::Log.debug(
31
+ "There are unmerged commits; use 'git branch -D #{branch}' to " +
32
+ 'forcefully delete it)',
33
+ )
34
+ end
35
+ end
36
+
37
+ # Return to the branch we were on, or main
38
+ if all_local_branches.include?(curr)
39
+ git('checkout', curr)
40
+ else
41
+ checkout_main_branch
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def clean_branch(name)
48
+ die("Cannot remove #{name} branch") if MAIN_BRANCHES.include?(name)
49
+ SugarJar::Log.debug('Fetch relevant remote...')
50
+ fetch_upstream
51
+ return false unless safe_to_clean(name)
52
+
53
+ SugarJar::Log.debug('branch deemed safe to delete...')
54
+ checkout_main_branch
55
+ git('branch', '-D', name)
56
+ rebase
57
+ true
58
+ end
59
+
60
+ def safe_to_clean(branch)
61
+ # cherry -v will output 1 line per commit on the target branch
62
+ # prefixed by a - or + - anything with a - can be dropped, anything
63
+ # else cannot.
64
+ out = git(
65
+ 'cherry', '-v', tracked_branch, branch
66
+ ).stdout.lines.reject do |line|
67
+ line.start_with?('-')
68
+ end
69
+ if out.empty?
70
+ SugarJar::Log.debug(
71
+ "cherry-pick shows branch #{branch} obviously safe to delete",
72
+ )
73
+ return true
74
+ end
75
+
76
+ # if the "easy" check didn't work, it's probably because there
77
+ # was a squash-merge. To check for that we make our own squash
78
+ # merge to upstream/main and see if that has any delta
79
+
80
+ # First we need a temp branch to work on
81
+ tmpbranch = "_sugar_jar.#{Process.pid}"
82
+
83
+ git('checkout', '-b', tmpbranch, tracked_branch)
84
+ s = git_nofail('merge', '--squash', branch)
85
+ if s.error?
86
+ cleanup_tmp_branch(tmpbranch, branch)
87
+ SugarJar::Log.debug(
88
+ 'Failed to merge changes into current main. This means we could ' +
89
+ 'not figure out if this is merged or not. Check manually and use ' +
90
+ "'git branch -D #{branch}' if it is safe to do so.",
91
+ )
92
+ return false
93
+ end
94
+
95
+ s = git('diff', '--staged')
96
+ out = s.stdout
97
+ SugarJar::Log.debug("Squash-merged diff: #{out}")
98
+ cleanup_tmp_branch(tmpbranch, branch)
99
+ if out.empty?
100
+ SugarJar::Log.debug(
101
+ 'After squash-merging, this branch appears safe to delete',
102
+ )
103
+ true
104
+ else
105
+ SugarJar::Log.debug(
106
+ 'After squash-merging, this branch is NOT fully merged to main',
107
+ )
108
+ false
109
+ end
110
+ end
111
+
112
+ def cleanup_tmp_branch(tmp, backto)
113
+ git('reset', '--hard', tracked_branch)
114
+ git('checkout', backto)
115
+ git('branch', '-D', tmp)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,42 @@
1
+ class SugarJar
2
+ class Commands
3
+ def checkout(*args)
4
+ assert_in_repo!
5
+ # Pop the last arguement, which is _probably_ a branch name
6
+ # and then add any featureprefix, and if _that_ is a branch
7
+ # name, replace the last arguement with that
8
+ name = args.last
9
+ bname = fprefix(name)
10
+ if all_local_branches.include?(bname)
11
+ SugarJar::Log.debug("Featurepefixing #{name} -> #{bname}")
12
+ args[-1] = bname
13
+ end
14
+ s = git('checkout', *args)
15
+ SugarJar::Log.info(s.stderr + s.stdout.chomp)
16
+ end
17
+ alias co checkout
18
+
19
+ def br
20
+ assert_in_repo!
21
+ SugarJar::Log.info(git('branch', '-v').stdout.chomp)
22
+ end
23
+
24
+ def binfo
25
+ assert_in_repo!
26
+ SugarJar::Log.info(git(
27
+ 'log', '--graph', '--oneline', '--decorate', '--boundary',
28
+ "#{tracked_branch}.."
29
+ ).stdout.chomp)
30
+ end
31
+
32
+ # binfo for all branches
33
+ def smartlog
34
+ assert_in_repo!
35
+ SugarJar::Log.info(git(
36
+ 'log', '--graph', '--oneline', '--decorate', '--boundary',
37
+ '--branches', "#{most_main}.."
38
+ ).stdout.chomp)
39
+ end
40
+ alias sl smartlog
41
+ end
42
+ end
@@ -0,0 +1,139 @@
1
+ require_relative '../util'
2
+
3
+ class SugarJar
4
+ class Commands
5
+ def lint
6
+ assert_in_repo!
7
+ if dirty?
8
+ if @ignore_dirty
9
+ SugarJar::Log.warn(
10
+ 'Your repo is dirty, but --ignore-dirty was specified, so ' +
11
+ 'carrying on anyway. If the linter autocorrects, the displayed ' +
12
+ 'diff will be misleading',
13
+ )
14
+ else
15
+ SugarJar::Log.error(
16
+ 'Your repo is dirty, but --ignore-dirty was not specified. ' +
17
+ 'Refusing to run lint. This is to ensure that if the linter ' +
18
+ 'autocorrects, we can show the correct diff.',
19
+ )
20
+ exit(1)
21
+ end
22
+ end
23
+ exit(1) unless run_check('lint')
24
+ end
25
+
26
+ def unit
27
+ assert_in_repo!
28
+ exit(1) unless run_check('unit')
29
+ end
30
+
31
+ def get_checks_from_command(type)
32
+ return nil unless @repo_config["#{type}_list_cmd"]
33
+
34
+ cmd = @repo_config["#{type}_list_cmd"]
35
+ short = cmd.split.first
36
+ unless File.exist?(short)
37
+ SugarJar::Log.error(
38
+ "Configured #{type}_list_cmd #{short} does not exist!",
39
+ )
40
+ return false
41
+ end
42
+ s = Mixlib::ShellOut.new(cmd).run_command
43
+ if s.error?
44
+ SugarJar::Log.error(
45
+ "#{type}_list_cmd (#{cmd}) failed: #{s.format_for_exception}",
46
+ )
47
+ return false
48
+ end
49
+ s.stdout.split("\n")
50
+ end
51
+
52
+ # determine if we're using the _list_cmd and if so run it to get the
53
+ # checks, or just use the directly-defined check, and cache it
54
+ def get_checks(type)
55
+ return @checks[type] if @checks[type]
56
+
57
+ ret = get_checks_from_command(type)
58
+ if ret
59
+ SugarJar::Log.debug("Found #{type}s: #{ret}")
60
+ @checks[type] = ret
61
+ # if it's explicitly false, we failed to run the command
62
+ elsif ret == false
63
+ @checks[type] = false
64
+ # otherwise, we move on (basically: it's nil, there was no _list_cmd)
65
+ else
66
+ SugarJar::Log.debug("[#{type}]: using listed linters: #{ret}")
67
+ @checks[type] = @repo_config[type] || []
68
+ end
69
+ @checks[type]
70
+ end
71
+
72
+ def run_check(type)
73
+ repo_root = SugarJar::Util.repo_root
74
+ Dir.chdir repo_root do
75
+ checks = get_checks(type)
76
+ # if we failed to determine the checks, the the checks have effectively
77
+ # failed
78
+ return false unless checks
79
+
80
+ checks.each do |check|
81
+ SugarJar::Log.debug("Running #{type} #{check}")
82
+
83
+ short = check.split.first
84
+ if short.include?('/')
85
+ short = File.join(repo_root, short) unless short.start_with?('/')
86
+ unless File.exist?(short)
87
+ SugarJar::Log.error("Configured #{type} #{short} does not exist!")
88
+ end
89
+ elsif !SugarJar::Util.which_nofail(short)
90
+ SugarJar::Log.error("Configured #{type} #{short} does not exist!")
91
+ return false
92
+ end
93
+ s = Mixlib::ShellOut.new(check).run_command
94
+
95
+ # Linters auto-correct, lets handle that gracefully
96
+ if type == 'lint' && dirty?
97
+ SugarJar::Log.info(
98
+ "[#{type}] #{short}: #{color('Corrected', :yellow)}",
99
+ )
100
+ SugarJar::Log.warn(
101
+ "The linter modified the repo. Here's the diff:\n",
102
+ )
103
+ puts git('diff').stdout
104
+ loop do
105
+ $stdout.print(
106
+ "\nWould you like to\n\t[q]uit and inspect\n\t[a]mend the " +
107
+ "changes to the current commit and re-run\n > ",
108
+ )
109
+ ans = $stdin.gets.strip
110
+ case ans
111
+ when /^q/
112
+ SugarJar::Log.info('Exiting at user request.')
113
+ exit(1)
114
+ when /^a/
115
+ qamend('-a')
116
+ # break here, if we get out of this loop we 'redo', assuming
117
+ # the user chose this option
118
+ break
119
+ end
120
+ end
121
+ redo
122
+ end
123
+
124
+ if s.error?
125
+ SugarJar::Log.info(
126
+ "[#{type}] #{short} #{color('failed', :red)}, output follows " +
127
+ "(see debug for more)\n#{s.stdout}",
128
+ )
129
+ SugarJar::Log.debug(s.format_for_exception)
130
+ return false
131
+ end
132
+ SugarJar::Log.info(
133
+ "[#{type}] #{short}: #{color('OK', :green)}",
134
+ )
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,16 @@
1
+ require 'json'
2
+
3
+ class SugarJar
4
+ class Commands
5
+ def debuginfo(*args)
6
+ puts "sugarjar version #{SugarJar::VERSION}"
7
+ puts ghcli('version').stdout
8
+ puts git('version').stdout
9
+
10
+ puts "Config: #{JSON.pretty_generate(args[0])}"
11
+ return unless @repo_config
12
+
13
+ puts "Repo config: #{JSON.pretty_generate(@repo_config)}"
14
+ end
15
+ end
16
+ end