sugarjar 3.0.0 → 4.0.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/README.md +131 -22
- data/bin/sj +49 -178
- data/examples/sample_config.yaml +11 -5
- data/examples/sample_repoconfig.yaml +8 -19
- data/extras/sugarjar_completion.bash +14 -11
- data/lib/sugarjar/commands/checks.rb +33 -11
- data/lib/sugarjar/commands/debuginfo.rb +3 -1
- data/lib/sugarjar/commands/modernize_config.rb +119 -0
- data/lib/sugarjar/commands/push.rb +1 -1
- data/lib/sugarjar/commands/smartclone.rb +54 -16
- data/lib/sugarjar/commands/smartpullrequest.rb +31 -21
- data/lib/sugarjar/commands.rb +111 -97
- data/lib/sugarjar/config.rb +53 -35
- data/lib/sugarjar/forge.rb +98 -0
- data/lib/sugarjar/git.rb +29 -0
- data/lib/sugarjar/help.rb +313 -0
- data/lib/sugarjar/repoconfig.rb +3 -3
- data/lib/sugarjar/util.rb +0 -65
- data/lib/sugarjar/version.rb +1 -1
- data/sugarjar.gemspec +1 -0
- metadata +20 -2
data/lib/sugarjar/commands.rb
CHANGED
|
@@ -6,14 +6,16 @@ require_relative 'commands/branch'
|
|
|
6
6
|
require_relative 'commands/checks'
|
|
7
7
|
require_relative 'commands/debuginfo'
|
|
8
8
|
require_relative 'commands/feature'
|
|
9
|
+
require_relative 'commands/modernize_config'
|
|
9
10
|
require_relative 'commands/pullsuggestions'
|
|
10
11
|
require_relative 'commands/push'
|
|
11
12
|
require_relative 'commands/smartclone'
|
|
12
13
|
require_relative 'commands/smartpullrequest'
|
|
13
14
|
require_relative 'commands/up'
|
|
15
|
+
require_relative 'forge'
|
|
16
|
+
require_relative 'git'
|
|
14
17
|
require_relative 'log'
|
|
15
18
|
require_relative 'repoconfig'
|
|
16
|
-
require_relative 'util'
|
|
17
19
|
require_relative 'version'
|
|
18
20
|
|
|
19
21
|
class SugarJar
|
|
@@ -25,37 +27,21 @@ class SugarJar
|
|
|
25
27
|
|
|
26
28
|
def initialize(options)
|
|
27
29
|
SugarJar::Log.debug("Commands.initialize options: #{options}")
|
|
28
|
-
@
|
|
29
|
-
@ignore_prerun_failure = options['ignore_prerun_failure']
|
|
30
|
+
@config = options
|
|
30
31
|
@repo_config = SugarJar::RepoConfig.config
|
|
31
32
|
SugarJar::Log.debug("Repoconfig: #{@repo_config}")
|
|
32
|
-
|
|
33
|
-
@pr_autofill = options['pr_autofill']
|
|
34
|
-
@pr_autostack = options['pr_autostack']
|
|
35
|
-
@feature_prefix = options['feature_prefix']
|
|
33
|
+
|
|
36
34
|
@checks = {}
|
|
37
35
|
@main_branch = nil
|
|
38
36
|
@main_remote_branches = {}
|
|
39
|
-
|
|
40
|
-
# to the method forge_host which will always return something
|
|
41
|
-
@_forge_host = @repo_config['forge_host'] || options['forge_host']
|
|
42
|
-
@repo_forge = @repo_config['forge_type'] || options['forge_type'] ||
|
|
43
|
-
_determine_forge_type
|
|
44
|
-
|
|
45
|
-
unless @repo_forge.nil?
|
|
46
|
-
cmd = _forge_cmd
|
|
47
|
-
unless SugarJar::Util.which_nofail(cmd)
|
|
48
|
-
die("No '#{cmd}' found, please install it'")
|
|
49
|
-
end
|
|
50
|
-
end
|
|
37
|
+
@host_config = nil
|
|
51
38
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
ENV['GL_HOST'] = @_forge_host
|
|
39
|
+
if SugarJar::Git.in_repo?
|
|
40
|
+
@host_config = gen_host_config
|
|
41
|
+
if @host_config['forge_type']
|
|
42
|
+
# initialize instance var here so checks run...
|
|
43
|
+
forge
|
|
44
|
+
end
|
|
59
45
|
end
|
|
60
46
|
|
|
61
47
|
return if options['no_change']
|
|
@@ -65,54 +51,85 @@ class SugarJar
|
|
|
65
51
|
|
|
66
52
|
private
|
|
67
53
|
|
|
54
|
+
def forge
|
|
55
|
+
return @forge if @forge
|
|
56
|
+
unless @host_config['forge_type']
|
|
57
|
+
raise 'Attempted to create Forge object before forge-type known!'
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
@forge = SugarJar::Forge.new(
|
|
61
|
+
@host_config['forge_type'], @host_config['forge_host']
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def gen_host_config(host = nil)
|
|
66
|
+
host ||= _determine_forge_host
|
|
67
|
+
_config_for_host(host) || {}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def _config_for_host(host)
|
|
71
|
+
SugarJar::Log.debug("config_for_host called for #{host}")
|
|
72
|
+
r = @config.dig('host_configs', 'default').dup || {}
|
|
73
|
+
r.merge!(@config.dig('host_configs', host) || {})
|
|
74
|
+
r['forge_host'] = host
|
|
75
|
+
# if people specified a forge_type, honor it
|
|
76
|
+
r['forge_type'] ||= _determine_forge_type(host)
|
|
77
|
+
|
|
78
|
+
# there are a few things we allow the CLI to override
|
|
79
|
+
r.merge!(@config['_cli_overrides'] || {})
|
|
80
|
+
|
|
81
|
+
SugarJar::Log.debug("Determined config for host: #{r}")
|
|
82
|
+
r
|
|
83
|
+
end
|
|
84
|
+
|
|
68
85
|
def forked_repo(repo, username)
|
|
86
|
+
host = @host_config&.dig('forge_host') || extract_host(repo) ||
|
|
87
|
+
@config['default_forge_host']
|
|
88
|
+
raise 'Cannot determine host' unless host
|
|
89
|
+
|
|
69
90
|
repo = extract_repo(repo)
|
|
70
|
-
|
|
91
|
+
|
|
92
|
+
"git@#{host}:#{username}/#{repo}.git"
|
|
71
93
|
end
|
|
72
94
|
|
|
73
95
|
# gh utils will default to https, but we should always default to SSH
|
|
74
96
|
# unless otherwise specified since https will cause prompting.
|
|
75
97
|
def canonicalize_repo(repo)
|
|
76
98
|
# if they fully-qualified it, we're good
|
|
77
|
-
return repo if repo.start_with?('http', 'git@')
|
|
99
|
+
return repo if repo.start_with?('http', 'git@', 'ssh://')
|
|
78
100
|
|
|
101
|
+
host = @host_config&.dig('forge_host') || extract_host(repo) ||
|
|
102
|
+
@config['default_forge_host']
|
|
79
103
|
# otherwise, it's a shortname
|
|
80
|
-
cr = "git@#{
|
|
104
|
+
cr = "git@#{host}:#{repo}.git"
|
|
81
105
|
SugarJar::Log.debug("canonicalized #{repo} to #{cr}")
|
|
82
106
|
cr
|
|
83
107
|
end
|
|
84
108
|
|
|
85
|
-
def
|
|
86
|
-
#
|
|
87
|
-
|
|
109
|
+
def _determine_forge_host
|
|
110
|
+
# DO NOT USE in the general case - ONLY for filling in @host_config
|
|
111
|
+
# (doesn't consult host_config). Use @host_config['forge_host']
|
|
112
|
+
# in general.
|
|
88
113
|
|
|
89
|
-
#
|
|
90
|
-
if SugarJar::
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
@repo_forge == 'gitlab' ? 'gitlab.com' : 'github.com'
|
|
114
|
+
# if we're in a repo, use the hostname of the remote
|
|
115
|
+
if SugarJar::Git.in_repo?
|
|
116
|
+
url = remote_url_map.values.first
|
|
117
|
+
return extract_host(url)
|
|
94
118
|
end
|
|
119
|
+
@config['default_forge_host']
|
|
95
120
|
end
|
|
96
121
|
|
|
97
122
|
def repo_shortname(repo)
|
|
98
123
|
# if it's already a shortname, return
|
|
99
124
|
return repo unless repo.start_with?('http', 'git@')
|
|
100
125
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
elsif repo.start_with?('git@')
|
|
105
|
-
relevant = repo.split(':').last
|
|
106
|
-
bits = relevant.split('/')
|
|
107
|
-
end
|
|
108
|
-
repo = bits[-1].gsub('.git', '')
|
|
109
|
-
org = bits[-2]
|
|
110
|
-
|
|
111
|
-
"#{org}/#{repo}"
|
|
126
|
+
repo_name = extract_repo(repo)
|
|
127
|
+
org_name = extract_org(repo)
|
|
128
|
+
[org_name, repo_name].join('/')
|
|
112
129
|
end
|
|
113
130
|
|
|
114
131
|
def set_commit_template
|
|
115
|
-
unless SugarJar::
|
|
132
|
+
unless SugarJar::Git.in_repo?
|
|
116
133
|
SugarJar::Log.debug('Skipping set_commit_template: not in repo')
|
|
117
134
|
return
|
|
118
135
|
end
|
|
@@ -120,7 +137,10 @@ class SugarJar
|
|
|
120
137
|
realpath = if @repo_config['commit_template'].start_with?('/')
|
|
121
138
|
@repo_config['commit_template']
|
|
122
139
|
else
|
|
123
|
-
|
|
140
|
+
File.join(
|
|
141
|
+
SugarJar::Git.repo_root,
|
|
142
|
+
@repo_config['commit_template'],
|
|
143
|
+
)
|
|
124
144
|
end
|
|
125
145
|
unless File.exist?(realpath)
|
|
126
146
|
die(
|
|
@@ -153,7 +173,7 @@ class SugarJar
|
|
|
153
173
|
end
|
|
154
174
|
|
|
155
175
|
def assert_in_repo!
|
|
156
|
-
return if SugarJar::
|
|
176
|
+
return if SugarJar::Git.in_repo?
|
|
157
177
|
|
|
158
178
|
die('sugarjar must be run from inside a git repo')
|
|
159
179
|
end
|
|
@@ -161,7 +181,7 @@ class SugarJar
|
|
|
161
181
|
def dirty_check!
|
|
162
182
|
return unless dirty?
|
|
163
183
|
|
|
164
|
-
if @ignore_dirty
|
|
184
|
+
if @config['ignore_dirty']
|
|
165
185
|
SugarJar::Log.warn(
|
|
166
186
|
'Your repo is dirty, but --ignore-dirty was specified, so ' +
|
|
167
187
|
'carrying on anyway.',
|
|
@@ -310,8 +330,14 @@ class SugarJar
|
|
|
310
330
|
extract_org(url)
|
|
311
331
|
end
|
|
312
332
|
|
|
333
|
+
def repo_name
|
|
334
|
+
# Take the url of the most-upstream remote, then pull the repo
|
|
335
|
+
# name from that
|
|
336
|
+
extract_repo(remote_url_map[upstream])
|
|
337
|
+
end
|
|
338
|
+
|
|
313
339
|
def color(string, *colors)
|
|
314
|
-
if @color
|
|
340
|
+
if @config['color']
|
|
315
341
|
pastel.decorate(string, *colors)
|
|
316
342
|
else
|
|
317
343
|
string
|
|
@@ -325,17 +351,13 @@ class SugarJar
|
|
|
325
351
|
end
|
|
326
352
|
end
|
|
327
353
|
|
|
328
|
-
def forge_cli_avail?
|
|
329
|
-
!!SugarJar::Util.which_nofail(_forge_cmd)
|
|
330
|
-
end
|
|
331
|
-
|
|
332
354
|
def fprefix(name)
|
|
333
|
-
return name unless @feature_prefix
|
|
355
|
+
return name unless @host_config['feature_prefix']
|
|
334
356
|
|
|
335
|
-
return name if name.start_with?(@feature_prefix)
|
|
357
|
+
return name if name.start_with?(@host_config['feature_prefix'])
|
|
336
358
|
return name if all_local_branches.include?(name)
|
|
337
359
|
|
|
338
|
-
newname = "#{@feature_prefix}#{name}"
|
|
360
|
+
newname = "#{@host_config['feature_prefix']}#{name}"
|
|
339
361
|
SugarJar::Log.debug(
|
|
340
362
|
"Munging feature name: #{name} -> #{newname} due to feature prefix",
|
|
341
363
|
)
|
|
@@ -347,18 +369,18 @@ class SugarJar
|
|
|
347
369
|
s.error?
|
|
348
370
|
end
|
|
349
371
|
|
|
350
|
-
def
|
|
351
|
-
SugarJar::
|
|
372
|
+
def repo_dir_name
|
|
373
|
+
SugarJar::Git.repo_root.split('/').last
|
|
352
374
|
end
|
|
353
375
|
|
|
354
376
|
def extract_org(repo)
|
|
355
377
|
if repo.start_with?('http')
|
|
356
|
-
|
|
378
|
+
repo.split('://').last.split('/')[1..-2].join('/')
|
|
357
379
|
elsif repo.start_with?('git@')
|
|
358
|
-
repo.split(':')
|
|
380
|
+
repo.split(':').last.split('/')[0..-2].join('/')
|
|
359
381
|
else
|
|
360
|
-
# assume they passed in a
|
|
361
|
-
repo.split('/').
|
|
382
|
+
# assume they passed in a cli-friendly shortname
|
|
383
|
+
repo.split('/')[0..-2].join('/')
|
|
362
384
|
end
|
|
363
385
|
end
|
|
364
386
|
|
|
@@ -369,6 +391,8 @@ class SugarJar
|
|
|
369
391
|
def extract_host(repo)
|
|
370
392
|
if repo.start_with?('git@')
|
|
371
393
|
repo.split(':').first.split('@').last
|
|
394
|
+
elsif repo.start_with?('ssh://')
|
|
395
|
+
repo.split('/')[2].split('@').last
|
|
372
396
|
elsif repo.start_with?('http')
|
|
373
397
|
repo.split('/')[2]
|
|
374
398
|
end
|
|
@@ -390,7 +414,7 @@ class SugarJar
|
|
|
390
414
|
end
|
|
391
415
|
|
|
392
416
|
def worktrees
|
|
393
|
-
root = SugarJar::
|
|
417
|
+
root = SugarJar::Git.repo_root
|
|
394
418
|
s = git('worktree', 'list', '--porcelain')
|
|
395
419
|
s.error!
|
|
396
420
|
worktrees = {}
|
|
@@ -421,43 +445,33 @@ class SugarJar
|
|
|
421
445
|
end
|
|
422
446
|
|
|
423
447
|
def git(*)
|
|
424
|
-
SugarJar::
|
|
448
|
+
SugarJar::Git.run(*, :color => @config['color'])
|
|
425
449
|
end
|
|
426
450
|
|
|
427
451
|
def git_nofail(*)
|
|
428
|
-
SugarJar::
|
|
429
|
-
end
|
|
430
|
-
|
|
431
|
-
def _determine_forge_type
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
452
|
+
SugarJar::Git.run_nofail(*, :color => @config['color'])
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def _determine_forge_type(host = nil)
|
|
456
|
+
if host
|
|
457
|
+
case host
|
|
458
|
+
when /gitlab/
|
|
459
|
+
return 'gitlab'
|
|
460
|
+
when /github/
|
|
461
|
+
return 'github'
|
|
462
|
+
when /forgejo|codeberg/
|
|
463
|
+
return 'forgejo'
|
|
464
|
+
end
|
|
440
465
|
end
|
|
441
|
-
end
|
|
442
|
-
|
|
443
|
-
def _forge_cmd
|
|
444
|
-
@repo_forge == 'gitlab' ? 'glab' : 'gh'
|
|
445
|
-
end
|
|
446
466
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
else
|
|
451
|
-
SugarJar::Util.ghcli(*)
|
|
467
|
+
if SugarJar::Git.in_repo?
|
|
468
|
+
gl = remote_url_map.values.any? { |x| x.include?('gitlab') }
|
|
469
|
+
return gl ? 'gitlab' : 'github'
|
|
452
470
|
end
|
|
453
|
-
end
|
|
454
471
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
else
|
|
459
|
-
SugarJar::Util.ghcli_nofail(*)
|
|
460
|
-
end
|
|
472
|
+
# in smartclone mode, when creating the object, we will in fact
|
|
473
|
+
# be unable to determine this, that's expacted
|
|
474
|
+
SugarJar::Log.debug('Unable to determine forge_type!')
|
|
461
475
|
end
|
|
462
476
|
end
|
|
463
477
|
end
|
data/lib/sugarjar/config.rb
CHANGED
|
@@ -1,24 +1,52 @@
|
|
|
1
1
|
require 'yaml'
|
|
2
2
|
require_relative 'log'
|
|
3
|
+
require 'deep_merge'
|
|
3
4
|
|
|
4
5
|
class SugarJar
|
|
5
6
|
# This parses SugarJar configs (not to be confused with repoconfigs).
|
|
6
7
|
# This is stuff like log level, github-user, etc.
|
|
7
8
|
class Config
|
|
8
9
|
DEFAULTS = {
|
|
9
|
-
'
|
|
10
|
-
'gitlab_user' => ENV.fetch('USER'),
|
|
10
|
+
'default_forge_host' => nil,
|
|
11
11
|
'pr_autofill' => true,
|
|
12
12
|
'pr_autostack' => nil,
|
|
13
13
|
'color' => true,
|
|
14
14
|
'ignore_deprecated_options' => [],
|
|
15
|
+
'host_configs' => {
|
|
16
|
+
'default' => {
|
|
17
|
+
'user' => ENV.fetch('USER'),
|
|
18
|
+
'use_forks' => true,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
# things we have a command-line option for, but doesn't make sense
|
|
24
|
+
# in a config file.
|
|
25
|
+
EXTRA_CONFIGS = %w{fork_name force_forge_type}.freeze
|
|
26
|
+
|
|
27
|
+
TOP_LEVEL_CONFIGS = (DEFAULTS.keys + EXTRA_CONFIGS).freeze
|
|
28
|
+
|
|
29
|
+
DEPRECATED_OPTIONS = %w{
|
|
30
|
+
fallthru
|
|
31
|
+
gh_cli
|
|
32
|
+
github_user
|
|
33
|
+
gitlab_user
|
|
34
|
+
github_host
|
|
35
|
+
gitlab_host
|
|
15
36
|
}.freeze
|
|
16
37
|
|
|
17
38
|
def self._find_ordered_files
|
|
39
|
+
real_files = []
|
|
18
40
|
[
|
|
19
|
-
'/etc/sugarjar
|
|
20
|
-
"#{Dir.home}/.config/sugarjar
|
|
21
|
-
].
|
|
41
|
+
'/etc/sugarjar',
|
|
42
|
+
"#{Dir.home}/.config/sugarjar",
|
|
43
|
+
].each do |dir|
|
|
44
|
+
%w{yaml yml}.each do |ext|
|
|
45
|
+
f = File.join(dir, "config.#{ext}")
|
|
46
|
+
real_files << f if File.exist?(f)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
real_files
|
|
22
50
|
end
|
|
23
51
|
|
|
24
52
|
def self.config
|
|
@@ -27,21 +55,34 @@ class SugarJar
|
|
|
27
55
|
_find_ordered_files.each do |f|
|
|
28
56
|
SugarJar::Log.debug("Loading config #{f}")
|
|
29
57
|
data = YAML.safe_load_file(f)
|
|
58
|
+
next unless data
|
|
59
|
+
|
|
30
60
|
warn_on_deprecated_configs(data, f)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
data.delete('github_host')
|
|
34
|
-
end
|
|
35
|
-
# an empty file is a `nil` which you can't merge
|
|
36
|
-
c.merge!(YAML.safe_load_file(f)) if data
|
|
61
|
+
data = data.slice(*TOP_LEVEL_CONFIGS)
|
|
62
|
+
c.deep_merge!(data)
|
|
37
63
|
SugarJar::Log.debug("Modified config: #{c}")
|
|
38
64
|
end
|
|
39
65
|
c
|
|
40
66
|
end
|
|
41
67
|
|
|
42
68
|
def self.warn_on_deprecated_configs(data, fname)
|
|
69
|
+
# if it doesn't have host_configs **and** it has one of the
|
|
70
|
+
# previous user options, that were effectively required before
|
|
71
|
+
# and have now moved to host_configs, then we treat it as an
|
|
72
|
+
# old config
|
|
73
|
+
if %w{github_user gitlab_user}.any? { |useropt| data.key?(useropt) } &&
|
|
74
|
+
!data['host_configs']
|
|
75
|
+
SugarJar::Log.warn(
|
|
76
|
+
"#{fname} appears to be an old-style config.\nIt likely will " +
|
|
77
|
+
'not result in the behavior you want. You should run' +
|
|
78
|
+
"\n\n\tsj modernizeconfig #{fname}\n\n" +
|
|
79
|
+
"To automatically convert it.\n",
|
|
80
|
+
)
|
|
81
|
+
return
|
|
82
|
+
end
|
|
83
|
+
|
|
43
84
|
ignore_deprecated_options = data['ignore_deprecated_options'] || []
|
|
44
|
-
|
|
85
|
+
DEPRECATED_OPTIONS.each do |opt|
|
|
45
86
|
next unless data.key?(opt)
|
|
46
87
|
|
|
47
88
|
if ignore_deprecated_options.include?(opt)
|
|
@@ -56,29 +97,6 @@ class SugarJar
|
|
|
56
97
|
'suppress this warning with `ignore_deprecated_options`.',
|
|
57
98
|
)
|
|
58
99
|
end
|
|
59
|
-
|
|
60
|
-
# github_host has special handling
|
|
61
|
-
return unless data['github_host']
|
|
62
|
-
|
|
63
|
-
if ignore_deprecated_options.include?('github_host')
|
|
64
|
-
SugarJar::Log.debug(
|
|
65
|
-
"#{fname}: Deprecated option `github_host` found, but not " +
|
|
66
|
-
'warning due to `ignore_deprecated_options` in that file.',
|
|
67
|
-
)
|
|
68
|
-
elsif data.key?('forge_host')
|
|
69
|
-
SugarJar::Log.warn(
|
|
70
|
-
"#{fname}: Deprecated option `github_host` found. " +
|
|
71
|
-
'Ignoring in favor of newer `force_host` option. You can ' +
|
|
72
|
-
'suppress this warning with `ignore_deprecated_options`.',
|
|
73
|
-
)
|
|
74
|
-
else
|
|
75
|
-
SugarJar::Log.warn(
|
|
76
|
-
"#{fname}: Deprecated option `github_host` found. " +
|
|
77
|
-
'Treating it as if it was `forge_host` for now. Please update ' +
|
|
78
|
-
'your config file to use this new option. You can suppress ' +
|
|
79
|
-
'this warning with `ignore_deprecated_options`.',
|
|
80
|
-
)
|
|
81
|
-
end
|
|
82
100
|
end
|
|
83
101
|
end
|
|
84
102
|
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
require 'mixlib/shellout'
|
|
2
|
+
require_relative 'util'
|
|
3
|
+
require_relative 'log'
|
|
4
|
+
|
|
5
|
+
class SugarJar
|
|
6
|
+
class Forge
|
|
7
|
+
attr_reader :type
|
|
8
|
+
|
|
9
|
+
CMD_MAP = {
|
|
10
|
+
'github' => 'gh',
|
|
11
|
+
'gitlab' => 'glab',
|
|
12
|
+
'forgejo' => 'fj',
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
def initialize(type, host)
|
|
16
|
+
@type = type
|
|
17
|
+
@host = host
|
|
18
|
+
assert_cli_avail!
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def cmd
|
|
22
|
+
@cmd ||= CMD_MAP[@type]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def all_versions
|
|
26
|
+
CMD_MAP.values.map do |cli|
|
|
27
|
+
bin = SugarJar::Util.which_nofail(cli)
|
|
28
|
+
_forge_nofail(cli, 'version').stdout if bin
|
|
29
|
+
end.compact
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def run_nofail(*)
|
|
33
|
+
_forge_nofail(cmd, *)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def run(*)
|
|
37
|
+
s = _forge_nofail(cmd, *)
|
|
38
|
+
s.error!
|
|
39
|
+
s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def run_with_system(*args)
|
|
43
|
+
SugarJar::Log.trace("Running: #{cmd} #{args.join(' ')}")
|
|
44
|
+
system(cmd, *args)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def _forge_nofail(cli, *args)
|
|
50
|
+
SugarJar::Log.trace("Running: #{cli} #{args.join(' ')}")
|
|
51
|
+
bin = SugarJar::Util.which_nofail(cli)
|
|
52
|
+
s = Mixlib::ShellOut.new([bin] + args).run_command
|
|
53
|
+
if s.error? && ["#{cli} auth", 'Unauthorized'].any? do |err|
|
|
54
|
+
s.stderr.include?(err)
|
|
55
|
+
end
|
|
56
|
+
SugarJar::Log.info(
|
|
57
|
+
"#{cli} was run but no gitlab token exists. Will run " +
|
|
58
|
+
"'#{cli} auth login' to force\n#{cli} to authenticate...",
|
|
59
|
+
)
|
|
60
|
+
args = if cli == 'fj'
|
|
61
|
+
[bin, '-H', @host, 'auth', 'login']
|
|
62
|
+
else
|
|
63
|
+
[bin, 'auth', 'login', '-p', 'ssh']
|
|
64
|
+
end
|
|
65
|
+
unless system(*args)
|
|
66
|
+
SugarJar::Log.fatal(
|
|
67
|
+
"That failed, I will bail out. #{cli} needs to get a " +
|
|
68
|
+
"token. Try running '#{cli} auth login' (will list info about " +
|
|
69
|
+
'your account) and try this again when that works.',
|
|
70
|
+
)
|
|
71
|
+
exit(1)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if cli == 'fj'
|
|
75
|
+
fj = Mixlib::ShellOut.new([bin, 'auth', 'list']).run_command
|
|
76
|
+
unless fj.stdout.include?(@host)
|
|
77
|
+
SugarJar::Log.fatal(
|
|
78
|
+
'Logging in for you failed - this usually means you need to ' +
|
|
79
|
+
'create a token manually - the instructions should have been ' +
|
|
80
|
+
'printed above. Setup the token and then try again.',
|
|
81
|
+
)
|
|
82
|
+
exit(1)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
s
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def assert_cli_avail!
|
|
90
|
+
return if SugarJar::Util.which_nofail(cmd)
|
|
91
|
+
|
|
92
|
+
SugarJar::Log.error(
|
|
93
|
+
"Forge CLI #{cmd} is unavailable, please install",
|
|
94
|
+
)
|
|
95
|
+
exit 1
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
data/lib/sugarjar/git.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require_relative 'util'
|
|
2
|
+
|
|
3
|
+
class SugarJar
|
|
4
|
+
module Git
|
|
5
|
+
def self.run_nofail(*args, color: true)
|
|
6
|
+
if %w{diff log grep branch}.include?(args[0]) &&
|
|
7
|
+
args.none? { |x| x.include?('color') }
|
|
8
|
+
args << (color ? '--color' : '--no-color')
|
|
9
|
+
end
|
|
10
|
+
SugarJar::Log.trace("Running: git #{args.join(' ')}")
|
|
11
|
+
Mixlib::ShellOut.new([SugarJar::Util.which('git')] + args).run_command
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.run(*, color: true)
|
|
15
|
+
s = run_nofail(*, :color => color)
|
|
16
|
+
s.error!
|
|
17
|
+
s
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.in_repo?
|
|
21
|
+
s = run_nofail('rev-parse', '--is-inside-work-tree')
|
|
22
|
+
!s.error? && s.stdout.strip == 'true'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.repo_root
|
|
26
|
+
run('rev-parse', '--show-toplevel').stdout.strip
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|