sugarjar 2.0.0.beta.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +161 -0
- data/CONTRIBUTING.md +37 -0
- data/README.md +113 -187
- data/examples/sample_config.yaml +24 -0
- data/examples/sample_repoconfig.yaml +77 -0
- data/lib/sugarjar/commands/amend.rb +17 -0
- data/lib/sugarjar/commands/bclean.rb +118 -0
- data/lib/sugarjar/commands/branch.rb +42 -0
- data/lib/sugarjar/commands/checks.rb +139 -0
- data/lib/sugarjar/commands/debuginfo.rb +16 -0
- data/lib/sugarjar/commands/feature.rb +35 -0
- data/lib/sugarjar/commands/pullsuggestions.rb +48 -0
- data/lib/sugarjar/commands/push.rb +66 -0
- data/lib/sugarjar/commands/smartclone.rb +30 -0
- data/lib/sugarjar/commands/smartpullrequest.rb +101 -0
- data/lib/sugarjar/commands/up.rb +103 -0
- data/lib/sugarjar/commands.rb +284 -5
- data/lib/sugarjar/repoconfig.rb +2 -4
- data/lib/sugarjar/util.rb +20 -309
- data/lib/sugarjar/version.rb +1 -1
- data/sugarjar.gemspec +11 -5
- metadata +26 -7
data/lib/sugarjar/util.rb
CHANGED
@@ -1,289 +1,21 @@
|
|
1
|
-
require_relative 'log'
|
2
|
-
|
3
1
|
require 'mixlib/shellout'
|
4
2
|
|
3
|
+
require_relative 'log'
|
4
|
+
|
5
5
|
class SugarJar
|
6
|
-
# Some common methods needed by other classes
|
7
6
|
module Util
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
repo.split(':')[1].split('/')[0]
|
13
|
-
else
|
14
|
-
# assume they passed in a ghcli-friendly name
|
15
|
-
repo.split('/').first
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def extract_repo(repo)
|
20
|
-
File.basename(repo, '.git')
|
21
|
-
end
|
22
|
-
|
23
|
-
def forked_repo(repo, username)
|
24
|
-
repo = if repo.start_with?('http', 'git@')
|
25
|
-
File.basename(repo)
|
26
|
-
else
|
27
|
-
"#{File.basename(repo)}.git"
|
28
|
-
end
|
29
|
-
"git@#{@ghhost || 'github.com'}:#{username}/#{repo}"
|
30
|
-
end
|
31
|
-
|
32
|
-
# gh utils will default to https, but we should always default to SSH
|
33
|
-
# unless otherwise specified since https will cause prompting.
|
34
|
-
def canonicalize_repo(repo)
|
35
|
-
# if they fully-qualified it, we're good
|
36
|
-
return repo if repo.start_with?('http', 'git@')
|
37
|
-
|
38
|
-
# otherwise, ti's a shortname
|
39
|
-
cr = "git@#{@ghhost || 'github.com'}:#{repo}.git"
|
40
|
-
SugarJar::Log.debug("canonicalized #{repo} to #{cr}")
|
41
|
-
cr
|
42
|
-
end
|
43
|
-
|
44
|
-
def set_commit_template
|
45
|
-
unless in_repo
|
46
|
-
SugarJar::Log.debug('Skipping set_commit_template: not in repo')
|
47
|
-
return
|
48
|
-
end
|
49
|
-
|
50
|
-
realpath = if @repo_config['commit_template'].start_with?('/')
|
51
|
-
@repo_config['commit_template']
|
52
|
-
else
|
53
|
-
"#{repo_root}/#{@repo_config['commit_template']}"
|
54
|
-
end
|
55
|
-
unless File.exist?(realpath)
|
56
|
-
die(
|
57
|
-
"Repo config specifies #{@repo_config['commit_template']} as the " +
|
58
|
-
'commit template, but that file does not exist.',
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
|
-
s = git_nofail('config', '--local', 'commit.template')
|
63
|
-
unless s.error?
|
64
|
-
current = s.stdout.strip
|
65
|
-
if current == @repo_config['commit_template']
|
66
|
-
SugarJar::Log.debug('Commit template already set correctly')
|
67
|
-
return
|
68
|
-
else
|
69
|
-
SugarJar::Log.warn(
|
70
|
-
"Updating repo-specific commit template from #{current} " +
|
71
|
-
"to #{@repo_config['commit_template']}",
|
72
|
-
)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
SugarJar::Log.debug(
|
77
|
-
'Setting repo-specific commit template to ' +
|
78
|
-
"#{@repo_config['commit_template']} per sugarjar repo config.",
|
79
|
-
)
|
80
|
-
git(
|
81
|
-
'config', '--local', 'commit.template', @repo_config['commit_template']
|
82
|
-
)
|
83
|
-
end
|
84
|
-
|
85
|
-
def run_prepush
|
86
|
-
@repo_config['on_push']&.each do |item|
|
87
|
-
SugarJar::Log.debug("Running on_push check type #{item}")
|
88
|
-
unless send(:run_check, item)
|
89
|
-
SugarJar::Log.info("[prepush]: #{item} #{color('failed', :red)}.")
|
90
|
-
return false
|
91
|
-
end
|
92
|
-
end
|
93
|
-
true
|
94
|
-
end
|
7
|
+
# a mixin to hold stuff that Commands and RepoConfig both use
|
8
|
+
def self.which(cmd)
|
9
|
+
path = which_nofail(cmd)
|
10
|
+
return path if path
|
95
11
|
|
96
|
-
|
97
|
-
SugarJar::Log.fatal(msg)
|
12
|
+
SugarJar::Log.fatal("Could not find #{cmd} in your path")
|
98
13
|
exit(1)
|
99
14
|
end
|
100
15
|
|
101
|
-
def assert_common_main_branch
|
102
|
-
upstream_branch = main_remote_branch(upstream)
|
103
|
-
unless main_branch == upstream_branch
|
104
|
-
die(
|
105
|
-
"The local main branch is '#{main_branch}', but the main branch " +
|
106
|
-
"of the #{upstream} remote is '#{upstream_branch}'. You probably " +
|
107
|
-
"want to rename your local branch by doing:\n\t" +
|
108
|
-
"git branch -m #{main_branch} #{upstream_branch}\n\t" +
|
109
|
-
"git fetch #{upstream}\n\t" +
|
110
|
-
"git branch -u #{upstream}/#{upstream_branch} #{upstream_branch}\n" +
|
111
|
-
"\tgit remote set-head #{upstream} -a",
|
112
|
-
)
|
113
|
-
end
|
114
|
-
return if upstream_branch == 'origin'
|
115
|
-
|
116
|
-
origin_branch = main_remote_branch('origin')
|
117
|
-
return if origin_branch == upstream_branch
|
118
|
-
|
119
|
-
die(
|
120
|
-
"The main branch of your upstream (#{upstream_branch}) and your " +
|
121
|
-
"fork/origin (#{origin_branch}) are not the same. You should go " +
|
122
|
-
"to https://#{@ghhost || 'github.com'}/#{@ghuser}/#{repo_name}/" +
|
123
|
-
'branches/ and rename the \'default\' branch to ' +
|
124
|
-
"'#{upstream_branch}'. It will then give you some commands to " +
|
125
|
-
'run to update this clone.',
|
126
|
-
)
|
127
|
-
end
|
128
|
-
|
129
|
-
def assert_in_repo
|
130
|
-
die('sugarjar must be run from inside a git repo') unless in_repo
|
131
|
-
end
|
132
|
-
|
133
|
-
def determine_main_branch(branches)
|
134
|
-
branches.include?('main') ? 'main' : 'master'
|
135
|
-
end
|
136
|
-
|
137
|
-
def main_branch
|
138
|
-
@main_branch = determine_main_branch(all_local_branches)
|
139
|
-
end
|
140
|
-
|
141
|
-
def main_remote_branch(remote)
|
142
|
-
@main_remote_branches[remote] ||=
|
143
|
-
determine_main_branch(all_remote_branches(remote))
|
144
|
-
end
|
145
|
-
|
146
|
-
def checkout_main_branch
|
147
|
-
git('checkout', main_branch)
|
148
|
-
end
|
149
|
-
|
150
|
-
def all_remote_branches(remote = 'origin')
|
151
|
-
branches = []
|
152
|
-
git('branch', '-r', '--format', '%(refname)').stdout.lines.each do |line|
|
153
|
-
next unless line.start_with?("refs/remotes/#{remote}/")
|
154
|
-
|
155
|
-
branches << branch_from_ref(line.strip, :remote)
|
156
|
-
end
|
157
|
-
branches
|
158
|
-
end
|
159
|
-
|
160
|
-
def all_local_branches
|
161
|
-
git(
|
162
|
-
'branch', '--format', '%(refname)'
|
163
|
-
).stdout.lines.map do |line|
|
164
|
-
next if line.start_with?('(HEAD detached')
|
165
|
-
|
166
|
-
branch_from_ref(line.strip)
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def all_remotes
|
171
|
-
git('remote').stdout.lines.map(&:strip)
|
172
|
-
end
|
173
|
-
|
174
|
-
def current_branch
|
175
|
-
branch_from_ref(git('symbolic-ref', 'HEAD').stdout.strip)
|
176
|
-
end
|
177
|
-
|
178
|
-
def fetch_upstream
|
179
|
-
us = upstream
|
180
|
-
fetch(us) if us
|
181
|
-
end
|
182
|
-
|
183
|
-
def fetch(remote)
|
184
|
-
git('fetch', remote)
|
185
|
-
end
|
186
|
-
|
187
|
-
# determine if this branch is based on another local branch (i.e. is a
|
188
|
-
# subfeature). Used to figure out of we should stack the PR
|
189
|
-
def subfeature?(base)
|
190
|
-
all_local_branches.reject { |x| x == most_main }.include?(base)
|
191
|
-
end
|
192
|
-
|
193
|
-
def tracked_branch(fallback: true)
|
194
|
-
branch = nil
|
195
|
-
s = git_nofail(
|
196
|
-
'rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'
|
197
|
-
)
|
198
|
-
if s.error?
|
199
|
-
branch = fallback ? most_main : nil
|
200
|
-
SugarJar::Log.debug("No specific tracked branch, using #{branch}")
|
201
|
-
else
|
202
|
-
branch = s.stdout.strip
|
203
|
-
SugarJar::Log.debug(
|
204
|
-
"Using explicit tracked branch: #{branch}, use " +
|
205
|
-
'`git branch -u` to change',
|
206
|
-
)
|
207
|
-
end
|
208
|
-
branch
|
209
|
-
end
|
210
|
-
|
211
|
-
def most_main
|
212
|
-
us = upstream
|
213
|
-
if us
|
214
|
-
"#{us}/#{main_branch}"
|
215
|
-
else
|
216
|
-
main_branch
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
def upstream
|
221
|
-
return @remote if @remote
|
222
|
-
|
223
|
-
remotes = all_remotes
|
224
|
-
SugarJar::Log.debug("remotes is #{remotes}")
|
225
|
-
if remotes.empty?
|
226
|
-
@remote = nil
|
227
|
-
elsif remotes.length == 1
|
228
|
-
@remote = remotes[0]
|
229
|
-
elsif remotes.include?('upstream')
|
230
|
-
@remote = 'upstream'
|
231
|
-
elsif remotes.include?('origin')
|
232
|
-
@remote = 'origin'
|
233
|
-
else
|
234
|
-
raise 'Could not determine "upstream" remote to use...'
|
235
|
-
end
|
236
|
-
@remote
|
237
|
-
end
|
238
|
-
|
239
|
-
# Whatever org we push to, regardless of if this is a fork or not
|
240
|
-
def push_org
|
241
|
-
url = git('remote', 'get-url', 'origin').stdout.strip
|
242
|
-
extract_org(url)
|
243
|
-
end
|
244
|
-
|
245
|
-
def branch_from_ref(ref, type = :local)
|
246
|
-
# local branches are refs/head/XXXX
|
247
|
-
# remote branches are refs/remotes/<remote>/XXXX
|
248
|
-
base = type == :local ? 2 : 3
|
249
|
-
ref.split('/')[base..].join('/')
|
250
|
-
end
|
251
|
-
|
252
|
-
def color(string, *colors)
|
253
|
-
if @color
|
254
|
-
pastel.decorate(string, *colors)
|
255
|
-
else
|
256
|
-
string
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
def pastel
|
261
|
-
@pastel ||= begin
|
262
|
-
require 'pastel'
|
263
|
-
Pastel.new
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
def gh_avail?
|
268
|
-
!!which_nofail('gh')
|
269
|
-
end
|
270
|
-
|
271
|
-
def fprefix(name)
|
272
|
-
return name unless @feature_prefix
|
273
|
-
|
274
|
-
return name if name.start_with?(@feature_prefix)
|
275
|
-
return name if all_local_branches.include?(name)
|
276
|
-
|
277
|
-
newname = "#{@feature_prefix}#{name}"
|
278
|
-
SugarJar::Log.debug(
|
279
|
-
"Munging feature name: #{name} -> #{newname} due to feature prefix",
|
280
|
-
)
|
281
|
-
newname
|
282
|
-
end
|
283
|
-
|
284
16
|
# Finds the first entry in the path for a binary and checks
|
285
17
|
# to make sure it's not us. Warn if it is us as that won't work in 2.x
|
286
|
-
def which_nofail(cmd)
|
18
|
+
def self.which_nofail(cmd)
|
287
19
|
ENV['PATH'].split(File::PATH_SEPARATOR).each do |dir|
|
288
20
|
p = File.join(dir, cmd)
|
289
21
|
next unless File.exist?(p) && File.executable?(p)
|
@@ -299,43 +31,31 @@ class SugarJar
|
|
299
31
|
false
|
300
32
|
end
|
301
33
|
|
302
|
-
def
|
303
|
-
path = which_nofail(cmd)
|
304
|
-
return path if path
|
305
|
-
|
306
|
-
SugarJar::Log.fatal("Could not find #{cmd} in your path")
|
307
|
-
exit(1)
|
308
|
-
end
|
309
|
-
|
310
|
-
def git_nofail(*args)
|
34
|
+
def self.git_nofail(*args, color: true)
|
311
35
|
if %w{diff log grep branch}.include?(args[0]) &&
|
312
36
|
args.none? { |x| x.include?('color') }
|
313
|
-
args << (
|
37
|
+
args << (color ? '--color' : '--no-color')
|
314
38
|
end
|
315
39
|
SugarJar::Log.trace("Running: git #{args.join(' ')}")
|
316
40
|
Mixlib::ShellOut.new([which('git')] + args).run_command
|
317
41
|
end
|
318
42
|
|
319
|
-
def git(
|
320
|
-
s = git_nofail(
|
43
|
+
def self.git(*, color: true)
|
44
|
+
s = git_nofail(*, :color => color)
|
321
45
|
s.error!
|
322
46
|
s
|
323
47
|
end
|
324
48
|
|
325
|
-
def ghcli_nofail(*args)
|
49
|
+
def self.ghcli_nofail(*args)
|
326
50
|
SugarJar::Log.trace("Running: gh #{args.join(' ')}")
|
327
|
-
|
51
|
+
gh = which('gh')
|
52
|
+
s = Mixlib::ShellOut.new([gh] + args).run_command
|
328
53
|
if s.error? && s.stderr.include?('gh auth')
|
329
54
|
SugarJar::Log.info(
|
330
55
|
'gh was run but no github token exists. Will run "gh auth login" ' +
|
331
56
|
"to force\ngh to authenticate...",
|
332
57
|
)
|
333
|
-
|
334
|
-
args = [
|
335
|
-
which('gh'), 'auth', 'login', '-p', 'ssh'
|
336
|
-
]
|
337
|
-
args + ['--hostname', @ghhost] if @ghhost
|
338
|
-
unless system(which('gh'), 'auth', 'login', '-p', 'ssh')
|
58
|
+
unless system(gh, 'auth', 'login', '-p', 'ssh')
|
339
59
|
SugarJar::Log.fatal(
|
340
60
|
'That failed, I will bail out. Hub needs to get a github ' +
|
341
61
|
'token. Try running "gh auth login" (will list info about ' +
|
@@ -347,28 +67,19 @@ class SugarJar
|
|
347
67
|
s
|
348
68
|
end
|
349
69
|
|
350
|
-
def ghcli(*
|
351
|
-
s = ghcli_nofail(*
|
70
|
+
def self.ghcli(*)
|
71
|
+
s = ghcli_nofail(*)
|
352
72
|
s.error!
|
353
73
|
s
|
354
74
|
end
|
355
75
|
|
356
|
-
def in_repo
|
76
|
+
def self.in_repo?
|
357
77
|
s = git_nofail('rev-parse', '--is-inside-work-tree')
|
358
78
|
!s.error? && s.stdout.strip == 'true'
|
359
79
|
end
|
360
80
|
|
361
|
-
def
|
362
|
-
s = git_nofail('diff', '--quiet')
|
363
|
-
s.error?
|
364
|
-
end
|
365
|
-
|
366
|
-
def repo_root
|
81
|
+
def self.repo_root
|
367
82
|
git('rev-parse', '--show-toplevel').stdout.strip
|
368
83
|
end
|
369
|
-
|
370
|
-
def repo_name
|
371
|
-
repo_root.split('/').last
|
372
|
-
end
|
373
84
|
end
|
374
85
|
end
|
data/lib/sugarjar/version.rb
CHANGED
data/sugarjar.gemspec
CHANGED
@@ -8,14 +8,20 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.email = ['phil@ipom.com']
|
9
9
|
spec.license = 'Apache-2.0'
|
10
10
|
spec.homepage = 'https://github.com/jaymzh/sugarjar'
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
spec.required_ruby_version = '>= 3.2'
|
12
|
+
docs = %w{
|
13
|
+
README.md
|
14
|
+
LICENSE
|
15
|
+
Gemfile
|
16
|
+
sugarjar.gemspec
|
17
|
+
CONTRIBUTING.md
|
18
|
+
CHANGELOG.md
|
19
|
+
} + Dir.glob('examples/*')
|
15
20
|
spec.extra_rdoc_files = docs
|
16
21
|
spec.executables << 'sj'
|
17
22
|
spec.files =
|
18
23
|
Dir.glob('lib/sugarjar/*.rb') +
|
24
|
+
Dir.glob('lib/sugarjar/commands/*.rb') +
|
19
25
|
Dir.glob('bin/*') +
|
20
26
|
Dir.glob('extras/*')
|
21
27
|
|
@@ -28,7 +34,7 @@ Gem::Specification.new do |spec|
|
|
28
34
|
'bug_tracker_uri' => 'https://github.com/jaymzh/sugarjar/issues',
|
29
35
|
'changelog_uri' =>
|
30
36
|
'https://github.com/jaymzh/sugarjar/blob/main/CHANGELOG.md',
|
31
|
-
'homepage_uri' => 'https://github.com/jaymzh/
|
37
|
+
'homepage_uri' => 'https://github.com/jaymzh/sugarjar',
|
32
38
|
'source_code_uri' => 'https://github.com/jaymzh/sugarjar',
|
33
39
|
}
|
34
40
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sugarjar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Phil Dibowitz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deep_merge
|
@@ -77,13 +77,32 @@ extra_rdoc_files:
|
|
77
77
|
- LICENSE
|
78
78
|
- Gemfile
|
79
79
|
- sugarjar.gemspec
|
80
|
+
- CONTRIBUTING.md
|
81
|
+
- CHANGELOG.md
|
82
|
+
- examples/sample_config.yaml
|
83
|
+
- examples/sample_repoconfig.yaml
|
80
84
|
files:
|
85
|
+
- CHANGELOG.md
|
86
|
+
- CONTRIBUTING.md
|
81
87
|
- Gemfile
|
82
88
|
- LICENSE
|
83
89
|
- README.md
|
84
90
|
- bin/sj
|
91
|
+
- examples/sample_config.yaml
|
92
|
+
- examples/sample_repoconfig.yaml
|
85
93
|
- extras/sugarjar_completion.bash
|
86
94
|
- lib/sugarjar/commands.rb
|
95
|
+
- lib/sugarjar/commands/amend.rb
|
96
|
+
- lib/sugarjar/commands/bclean.rb
|
97
|
+
- lib/sugarjar/commands/branch.rb
|
98
|
+
- lib/sugarjar/commands/checks.rb
|
99
|
+
- lib/sugarjar/commands/debuginfo.rb
|
100
|
+
- lib/sugarjar/commands/feature.rb
|
101
|
+
- lib/sugarjar/commands/pullsuggestions.rb
|
102
|
+
- lib/sugarjar/commands/push.rb
|
103
|
+
- lib/sugarjar/commands/smartclone.rb
|
104
|
+
- lib/sugarjar/commands/smartpullrequest.rb
|
105
|
+
- lib/sugarjar/commands/up.rb
|
87
106
|
- lib/sugarjar/config.rb
|
88
107
|
- lib/sugarjar/log.rb
|
89
108
|
- lib/sugarjar/repoconfig.rb
|
@@ -97,7 +116,7 @@ metadata:
|
|
97
116
|
rubygems_mfa_required: 'true'
|
98
117
|
bug_tracker_uri: https://github.com/jaymzh/sugarjar/issues
|
99
118
|
changelog_uri: https://github.com/jaymzh/sugarjar/blob/main/CHANGELOG.md
|
100
|
-
homepage_uri: https://github.com/jaymzh/
|
119
|
+
homepage_uri: https://github.com/jaymzh/sugarjar
|
101
120
|
source_code_uri: https://github.com/jaymzh/sugarjar
|
102
121
|
post_install_message:
|
103
122
|
rdoc_options: []
|
@@ -107,14 +126,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
126
|
requirements:
|
108
127
|
- - ">="
|
109
128
|
- !ruby/object:Gem::Version
|
110
|
-
version: '3.
|
129
|
+
version: '3.2'
|
111
130
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
131
|
requirements:
|
113
|
-
- - "
|
132
|
+
- - ">="
|
114
133
|
- !ruby/object:Gem::Version
|
115
|
-
version:
|
134
|
+
version: '0'
|
116
135
|
requirements: []
|
117
|
-
rubygems_version: 3.
|
136
|
+
rubygems_version: 3.5.22
|
118
137
|
signing_key:
|
119
138
|
specification_version: 4
|
120
139
|
summary: A git/github helper script
|