sugarjar 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +68 -24
- data/bin/sj +23 -3
- data/lib/sugarjar/commands.rb +246 -58
- data/lib/sugarjar/config.rb +3 -2
- data/lib/sugarjar/util.rb +47 -3
- data/lib/sugarjar/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f4c70f80f34b048ba0d6b526e5eb20fcc51164b8ef78412a38ad5c953296898
|
4
|
+
data.tar.gz: 53958fab72a6c93d35617b63ffdb178cddcf1f97837a321533836896b0336c8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b3c546fe02babc8b186cf4f5de2d6a2716e371228af676dd5550d56b1f17db9d310d135f47a1905e77a98ed32ebd6716292bc19059a1e6e9e4733f725422900
|
7
|
+
data.tar.gz: 37df61e3b4161c2737dcf6392b5db3dcb8feef5d402868b48d6f28db9cb2254ddaf9b39d2719ea88071c4f37feb32efd9d327c105c3dcde428f495957e85913e
|
data/README.md
CHANGED
@@ -3,10 +3,11 @@
|
|
3
3
|
[![Lint](https://github.com/jaymzh/sugarjar/workflows/Lint/badge.svg)](https://github.com/jaymzh/sugarjar/actions?query=workflow%3ALint)
|
4
4
|
[![Unittest](https://github.com/jaymzh/sugarjar/workflows/Unittests/badge.svg)](https://github.com/jaymzh/sugarjar/actions?query=workflow%3AUnittests)
|
5
5
|
[![DCO](https://github.com/jaymzh/sugarjar/workflows/DCO%20Check/badge.svg)](https://github.com/jaymzh/sugarjar/actions?query=workflow%3A%22DCO+Check%22)
|
6
|
-
[![Gem Version](https://badge.fury.io/rb/sugarjar.svg)](https://badge.fury.io/rb/sugarjar)
|
7
6
|
|
8
|
-
Welcome to SugarJar - a git/github helper. It
|
9
|
-
[hub](https://hub.github.com/),
|
7
|
+
Welcome to SugarJar - a git/github helper. It needs one of the GitHub CLI's:
|
8
|
+
the current default is [hub](https://hub.github.com/), but there is
|
9
|
+
experimental support for [gh](https://cli.github.com/). So you will need one of
|
10
|
+
those two installed.
|
10
11
|
|
11
12
|
SugarJar is inspired by [arcanist](https://github.com/phacility/arcanist), and
|
12
13
|
its replacement at Facebook, JellyFish. Many of the features they provide for
|
@@ -19,6 +20,24 @@ If you miss Mondrian or Phabricator - this is the tool for you!
|
|
19
20
|
|
20
21
|
If you don't, there's a ton of useful stuff for everyone!
|
21
22
|
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
Sugarjar is packaged in a variety of Linux distributions - see if it's on the
|
26
|
+
list here, and if so, use your package manager (or `gem`) to install it:
|
27
|
+
|
28
|
+
[![Packaging status](https://repology.org/badge/vertical-allrepos/sugarjar.svg)](https://repology.org/project/sugarjar/versions)
|
29
|
+
|
30
|
+
Another option is to use our [omnibus
|
31
|
+
packages](https://github.com/jaymzh/sugarjar/releases). We keep Omnibus
|
32
|
+
packages for most distros that do not package Sugarjar. Omnibus packages are
|
33
|
+
distro packages (deb, rpm, etc.) that have all dependencies bundled up together
|
34
|
+
(including Ruby), and install in `/opt/sugarjar` allowing you to manage the
|
35
|
+
installation with your package manager, but with as a hermetically sealed app
|
36
|
+
that doesn't depend on the rest of your distro.
|
37
|
+
|
38
|
+
Finally, if none of those work for you, you can clone this repo and run it
|
39
|
+
directly from there.
|
40
|
+
|
22
41
|
## Auto cleanup squash-merged branches
|
23
42
|
|
24
43
|
It is common for a PR to go back and forth with a variety of nits, lint fixes,
|
@@ -73,7 +92,7 @@ This will:
|
|
73
92
|
* Clone your fork
|
74
93
|
* Add the original as an 'upstream' remote
|
75
94
|
|
76
|
-
Note that it takes
|
95
|
+
Note that it takes short names for repos. No need to specify a full URL,
|
77
96
|
just a $org/$repo.
|
78
97
|
|
79
98
|
Like `git clone`, `sj sclone` will accept an additional argument as the
|
@@ -198,6 +217,15 @@ smartlog` or `sj sl` for short.
|
|
198
217
|
|
199
218
|
![smartlog screenshot](https://github.com/jaymzh/sugarjar/blob/master/smartlog.png)
|
200
219
|
|
220
|
+
## Pulling in suggestions from the web
|
221
|
+
|
222
|
+
When someone 'suggests' a change in the GitHub WebUI, once you choose to commit
|
223
|
+
them, your origin and local branches are no longer in-sync. The
|
224
|
+
`pullsuggestions` command will attempt to merge in any remote commits to your
|
225
|
+
local branch. This command will show a diff and ask for confirmation before
|
226
|
+
attempting the merge and - if allowed to continue - will use a fast-forward
|
227
|
+
merge.
|
228
|
+
|
201
229
|
## And more!
|
202
230
|
|
203
231
|
See `sj help` for more commands!
|
@@ -205,8 +233,11 @@ See `sj help` for more commands!
|
|
205
233
|
## Using SugarJar as a git wrapper
|
206
234
|
|
207
235
|
SugarJar, by default, will pass any command it doesn't know straight to `hub`
|
208
|
-
(which passes commands **it** doesn't know to `git`).
|
209
|
-
to `
|
236
|
+
(which passes commands **it** doesn't know to `git`). If you have configured
|
237
|
+
SugarJar to use `gh` instead of `hub`, then it will pass commands straight to
|
238
|
+
`git` since `gh` doesn't act as a `git` wrapper.
|
239
|
+
|
240
|
+
As such you can alias it to `git` and just have a super-git.
|
210
241
|
|
211
242
|
```shell
|
212
243
|
$ alias git=sj
|
@@ -258,9 +289,15 @@ Sugarjar looks for a `.sugarjar.yaml` in the root of the repository to tell it
|
|
258
289
|
how to handle repo-specific things. Currently there options are:
|
259
290
|
|
260
291
|
* `lint` - A list of scripts to run on `sj lint`. These should be linters like
|
261
|
-
rubocop or pyflake.
|
292
|
+
rubocop or pyflake. Linters will be run from the root of the repo.
|
293
|
+
* `lint_list_cmd` - A command to run which will print out linters to run, one
|
294
|
+
per line. Takes precedence over `lint`. The command (and the resulting
|
295
|
+
linters) will be run from the root of the repo.
|
262
296
|
* `unit` - A list of scripts to run on `sj unit`. These should be unittest
|
263
|
-
runners like rspec or pyunit.
|
297
|
+
runners like rspec or pyunit. Test will be run fro mthe root of the repo.
|
298
|
+
* `unit_list_cmd` - A command to run which will print out the unit tests to
|
299
|
+
run, one more line. Takes precedence over `unit`. The command (and the
|
300
|
+
resulting unit tests) will be run from the root of the repo.
|
264
301
|
* `on_push` - A list of types (`lint`, `unit`) of checks to run before pushing.
|
265
302
|
It is highly recommended this is only `lint`. The goal here is to allow for
|
266
303
|
the user to get quick stylistic feedback before pushing their branch to avoid
|
@@ -312,21 +349,15 @@ sj clone jaymzh/sugarjar --github-host githuh.com
|
|
312
349
|
We will add the `hub.host` to the `sugarjar` clone so that future `hub` or `sj`
|
313
350
|
commands work without needing to specify..
|
314
351
|
|
315
|
-
##
|
352
|
+
## Support for `gh`
|
316
353
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
provide packages for Fedora, CentOS, Debian, and Ubuntu, but if you want
|
321
|
-
others, file an Issue. Since these packages are
|
322
|
-
[omnibus](https://github.com/chef/omnibus) packages which means they are bundled
|
323
|
-
with all of their dependencies. This means these packages will likely work as-is
|
324
|
-
on later releases of these distros or any similar distros.
|
354
|
+
As of version 0.11 there is experimental support for the `gh` CLI (instead of
|
355
|
+
`hub`). If you would like to use `gh`, install it and then add the following
|
356
|
+
to your configuration:
|
325
357
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
Finally you can clone the git repo and run it from within the repo if you'd like.
|
358
|
+
```yaml
|
359
|
+
github_cli: gh
|
360
|
+
```
|
330
361
|
|
331
362
|
## FAQ
|
332
363
|
|
@@ -340,6 +371,19 @@ and github, it seemed appropriate.
|
|
340
371
|
|
341
372
|
Why did you use `hub` instead of the newer `gh` CLI?
|
342
373
|
|
343
|
-
|
344
|
-
|
345
|
-
|
374
|
+
When I originally wrote SugarJar, `gh` was in early development, and `hub` had
|
375
|
+
many more features. Now that `gh` has matured, we have experimental support for
|
376
|
+
`gh` and will switch it to the default at some point.
|
377
|
+
|
378
|
+
In addition, I wanted SugarJar to be able to be a git wrapper, and so wrapping
|
379
|
+
`hub` allows us to do that but wrapping `gh` does not.
|
380
|
+
|
381
|
+
I'd like to package SugarJar for my favorite distro/OS, is that OK?
|
382
|
+
|
383
|
+
Of course! But I'd appreciate you emailing me to give me a heads up. Doing so
|
384
|
+
will allow me to make sure it shows up in the Repology badge above as well as
|
385
|
+
stop building Omnibus packages for whatever distro.
|
386
|
+
|
387
|
+
Does it work on Windows/Mac?
|
388
|
+
|
389
|
+
It should! Though I will admit I don't regularly test on non-Linux OSes.
|
data/bin/sj
CHANGED
@@ -33,8 +33,13 @@ parser = OptionParser.new do |opts|
|
|
33
33
|
options['fallthru'] = fallthru
|
34
34
|
end
|
35
35
|
|
36
|
-
opts.on(
|
37
|
-
|
36
|
+
opts.on(
|
37
|
+
'--github-cli CLI',
|
38
|
+
%w{gh cli},
|
39
|
+
'Github CLI to use ("gh" or "hub"). Note that support for "gh" is ' +
|
40
|
+
'currently experimental. default: "hub"',
|
41
|
+
) do |cli|
|
42
|
+
options['github_cli'] = cli
|
38
43
|
end
|
39
44
|
|
40
45
|
opts.on(
|
@@ -48,6 +53,10 @@ parser = OptionParser.new do |opts|
|
|
48
53
|
options['github_host'] = host
|
49
54
|
end
|
50
55
|
|
56
|
+
opts.on('--github-user USER', 'Github username') do |user|
|
57
|
+
options['github_user'] = user
|
58
|
+
end
|
59
|
+
|
51
60
|
opts.on('-h', '--help', 'Print this help message') do
|
52
61
|
puts opts
|
53
62
|
exit
|
@@ -128,6 +137,11 @@ COMMANDS:
|
|
128
137
|
lint
|
129
138
|
Run any linters configured in .sugarjar.yaml.
|
130
139
|
|
140
|
+
pullsuggestions, ps
|
141
|
+
Pull any suggestions *that have been committed* in the GitHub UI.
|
142
|
+
This will show the diff and prompt for confirmation before
|
143
|
+
merging. Note that a fast-forward merge will be used.
|
144
|
+
|
131
145
|
smartclone, sclone
|
132
146
|
A smart wrapper to "git clone" that handles forking and managing
|
133
147
|
remotes for you.
|
@@ -265,7 +279,13 @@ if is_valid_command
|
|
265
279
|
sj.send(subcommand.to_sym, *extra_opts)
|
266
280
|
elsif options['fallthru']
|
267
281
|
SugarJar::Log.debug("Falling thru to: hub #{ARGV.join(' ')}")
|
268
|
-
|
282
|
+
if options['github_cli'] == 'hub'
|
283
|
+
exec('hub', *ARGV)
|
284
|
+
else
|
285
|
+
# If we're using 'gh', it doesn't have 'git fall thru' support, so
|
286
|
+
# we pass thru directly to 'git'
|
287
|
+
exec('git', *ARGV)
|
288
|
+
end
|
269
289
|
else
|
270
290
|
SugarJar::Log.error("No such subcommand: #{subcommand}")
|
271
291
|
end
|
data/lib/sugarjar/commands.rb
CHANGED
@@ -18,24 +18,28 @@ class SugarJar
|
|
18
18
|
SugarJar::Log.debug("Commands.initialize options: #{options}")
|
19
19
|
@ghuser = options['github_user']
|
20
20
|
@ghhost = options['github_host']
|
21
|
+
@cli = options['github_cli']
|
21
22
|
@ignore_dirty = options['ignore_dirty']
|
22
23
|
@ignore_prerun_failure = options['ignore_prerun_failure']
|
23
24
|
@repo_config = SugarJar::RepoConfig.config
|
24
25
|
@color = options['color']
|
26
|
+
@checks = {}
|
27
|
+
@main_branch = nil
|
28
|
+
@main_remote_branches = {}
|
25
29
|
return if options['no_change']
|
26
30
|
|
27
|
-
set_hub_host
|
31
|
+
set_hub_host
|
28
32
|
set_commit_template if @repo_config['commit_template']
|
29
33
|
end
|
30
34
|
|
31
35
|
def feature(name, base = nil)
|
32
36
|
assert_in_repo
|
33
37
|
SugarJar::Log.debug("Feature: #{name}, #{base}")
|
34
|
-
die("#{name} already exists!") if
|
38
|
+
die("#{name} already exists!") if all_local_branches.include?(name)
|
35
39
|
base ||= most_main
|
36
40
|
base_pieces = base.split('/')
|
37
|
-
|
38
|
-
|
41
|
+
git('fetch', base_pieces[0]) if base_pieces.length > 1
|
42
|
+
git('checkout', '-b', name, base)
|
39
43
|
SugarJar::Log.info(
|
40
44
|
"Created feature branch #{color(name, :green)} based on " +
|
41
45
|
color(base, :green),
|
@@ -58,7 +62,7 @@ class SugarJar
|
|
58
62
|
def bcleanall
|
59
63
|
assert_in_repo
|
60
64
|
curr = current_branch
|
61
|
-
|
65
|
+
all_local_branches.each do |branch|
|
62
66
|
if MAIN_BRANCHES.include?(branch)
|
63
67
|
SugarJar::Log.debug("Skipping #{branch}")
|
64
68
|
next
|
@@ -76,8 +80,8 @@ class SugarJar
|
|
76
80
|
end
|
77
81
|
|
78
82
|
# Return to the branch we were on, or main
|
79
|
-
if
|
80
|
-
|
83
|
+
if all_local_branches.include?(curr)
|
84
|
+
git('checkout', curr)
|
81
85
|
else
|
82
86
|
checkout_main_branch
|
83
87
|
end
|
@@ -85,18 +89,18 @@ class SugarJar
|
|
85
89
|
|
86
90
|
def co(*args)
|
87
91
|
assert_in_repo
|
88
|
-
s =
|
92
|
+
s = git('checkout', *args)
|
89
93
|
SugarJar::Log.info(s.stderr + s.stdout.chomp)
|
90
94
|
end
|
91
95
|
|
92
96
|
def br
|
93
97
|
assert_in_repo
|
94
|
-
SugarJar::Log.info(
|
98
|
+
SugarJar::Log.info(git('branch', '-v').stdout.chomp)
|
95
99
|
end
|
96
100
|
|
97
101
|
def binfo
|
98
102
|
assert_in_repo
|
99
|
-
SugarJar::Log.info(
|
103
|
+
SugarJar::Log.info(git(
|
100
104
|
'log', '--graph', '--oneline', '--decorate', '--boundary',
|
101
105
|
"#{tracked_branch}.."
|
102
106
|
).stdout.chomp)
|
@@ -105,7 +109,7 @@ class SugarJar
|
|
105
109
|
# binfo for all branches
|
106
110
|
def smartlog
|
107
111
|
assert_in_repo
|
108
|
-
SugarJar::Log.info(
|
112
|
+
SugarJar::Log.info(git(
|
109
113
|
'log', '--graph', '--oneline', '--decorate', '--boundary',
|
110
114
|
'--branches', "#{most_main}.."
|
111
115
|
).stdout.chomp)
|
@@ -142,24 +146,24 @@ class SugarJar
|
|
142
146
|
|
143
147
|
def qamend(*args)
|
144
148
|
assert_in_repo
|
145
|
-
SugarJar::Log.info(
|
149
|
+
SugarJar::Log.info(git('commit', '--amend', '--no-edit', *args).stdout)
|
146
150
|
end
|
147
151
|
|
148
152
|
alias amendq qamend
|
149
153
|
|
150
154
|
def upall
|
151
155
|
assert_in_repo
|
152
|
-
|
156
|
+
all_local_branches.each do |branch|
|
153
157
|
next if MAIN_BRANCHES.include?(branch)
|
154
158
|
|
155
|
-
|
159
|
+
git('checkout', branch)
|
156
160
|
result = gitup
|
157
161
|
if result['so'].error?
|
158
162
|
SugarJar::Log.error(
|
159
163
|
"#{color(branch, :red)} failed rebase. Reverting attempt and " +
|
160
164
|
'moving to next branch. Try `sj up` manually on that branch.',
|
161
165
|
)
|
162
|
-
|
166
|
+
git('rebase', '--abort')
|
163
167
|
else
|
164
168
|
SugarJar::Log.info(
|
165
169
|
"#{color(branch, :green)} rebased on " +
|
@@ -176,21 +180,36 @@ class SugarJar
|
|
176
180
|
|
177
181
|
reponame = File.basename(repo, '.git')
|
178
182
|
dir ||= reponame
|
183
|
+
|
179
184
|
SugarJar::Log.info("Cloning #{reponame}...")
|
180
|
-
hub('clone', canonicalize_repo(repo), dir, *args)
|
181
185
|
|
186
|
+
# GH's 'fork' command (with the --clone arg) will fork, if necessary,
|
187
|
+
# then clone, and then setup the remotes with the appropriate names. So
|
188
|
+
# we just let it do all the work for us and return.
|
189
|
+
if gh?
|
190
|
+
ghcli('repo', 'fork', '--clone', canonicalize_repo(repo), dir, *args)
|
191
|
+
SugarJar::Log.info('Remotes "origin" and "upstream" configured.')
|
192
|
+
return
|
193
|
+
end
|
194
|
+
|
195
|
+
# For 'hub', first we clone, using git, as 'hub' always needs a repo
|
196
|
+
# to operate on.
|
197
|
+
git('clone', canonicalize_repo(repo), dir, *args)
|
198
|
+
|
199
|
+
# Then we go into it and attempt to use the 'fork' capability
|
182
200
|
Dir.chdir dir do
|
183
201
|
# Now that we have a repo, if we have a hub host set it.
|
184
|
-
set_hub_host
|
202
|
+
set_hub_host
|
185
203
|
|
186
204
|
org = extract_org(repo)
|
187
205
|
SugarJar::Log.debug("Comparing org #{org} to ghuser #{@ghuser}")
|
188
206
|
if org == @ghuser
|
189
207
|
puts 'Cloned forked or self-owned repo. Not creating "upstream".'
|
208
|
+
SugarJar::Log.info('Remotes "origin" and "upstream" configured.')
|
190
209
|
return
|
191
210
|
end
|
192
211
|
|
193
|
-
s =
|
212
|
+
s = ghcli_nofail('repo', 'fork', '--remote-name=origin')
|
194
213
|
if s.error?
|
195
214
|
if s.stdout.include?('SAML enforcement')
|
196
215
|
SugarJar::Log.info(
|
@@ -199,18 +218,18 @@ class SugarJar
|
|
199
218
|
)
|
200
219
|
exit(1)
|
201
220
|
else
|
202
|
-
#
|
203
|
-
# already existed. If we got an error, but didn't recognize
|
204
|
-
# that, we'll assume that's what happened and try to add the
|
205
|
-
#
|
221
|
+
# gh as well as old versions of hub, it would fail if the upstream
|
222
|
+
# fork already existed. If we got an error, but didn't recognize
|
223
|
+
# that, we'll assume that's what happened and try to add the remote
|
224
|
+
# ourselves.
|
206
225
|
SugarJar::Log.info("Fork (#{@ghuser}/#{reponame}) detected.")
|
207
226
|
SugarJar::Log.debug(
|
208
227
|
'The above is a bit of a lie. "hub" failed to fork and it was ' +
|
209
228
|
'not a SAML error, so our best guess is that a fork exists ' +
|
210
229
|
'and so we will try to configure it.',
|
211
230
|
)
|
212
|
-
|
213
|
-
|
231
|
+
git('remote', 'rename', 'origin', 'upstream')
|
232
|
+
git('remote', 'add', 'origin', forked_repo(repo, @ghuser))
|
214
233
|
end
|
215
234
|
else
|
216
235
|
SugarJar::Log.info("Forked #{reponame} to #{@ghuser}")
|
@@ -247,11 +266,15 @@ class SugarJar
|
|
247
266
|
|
248
267
|
def version
|
249
268
|
puts "sugarjar version #{SugarJar::VERSION}"
|
250
|
-
puts
|
269
|
+
puts ghcli('version').stdout
|
270
|
+
# 'hub' prints the 'git' version, but gh doesn't, so if we're on 'gh'
|
271
|
+
# print out the git version directly
|
272
|
+
puts git('version').stdout if gh?
|
251
273
|
end
|
252
274
|
|
253
|
-
def smartpullrequest
|
275
|
+
def smartpullrequest(*args)
|
254
276
|
assert_in_repo
|
277
|
+
assert_common_main_branch
|
255
278
|
if dirty?
|
256
279
|
SugarJar::Log.warn(
|
257
280
|
'Your repo is dirty, so I am not going to create a pull request. ' +
|
@@ -259,12 +282,61 @@ class SugarJar
|
|
259
282
|
)
|
260
283
|
exit(1)
|
261
284
|
end
|
262
|
-
|
285
|
+
if gh?
|
286
|
+
SugarJar::Log.trace("Running: gh pr create #{args.join(' ')}")
|
287
|
+
system(which('gh'), 'pr', 'create', *args)
|
288
|
+
else
|
289
|
+
SugarJar::Log.trace("Running: hub pull-request #{args.join(' ')}")
|
290
|
+
system(which('hub'), 'pull-request', *args)
|
291
|
+
end
|
263
292
|
end
|
264
293
|
|
265
294
|
alias spr smartpullrequest
|
266
295
|
alias smartpr smartpullrequest
|
267
296
|
|
297
|
+
def pullsuggestions
|
298
|
+
assert_in_repo
|
299
|
+
|
300
|
+
if dirty?
|
301
|
+
if @ignore_dirty
|
302
|
+
SugarJar::Log.warn(
|
303
|
+
'Your repo is dirty, but --ignore-dirty was specified, so ' +
|
304
|
+
'carrying on anyway.',
|
305
|
+
)
|
306
|
+
else
|
307
|
+
SugarJar::Log.error(
|
308
|
+
'Your repo is dirty, so I am not going to push. Please commit ' +
|
309
|
+
'or amend first.',
|
310
|
+
)
|
311
|
+
exit(1)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
src = "origin/#{current_branch}"
|
316
|
+
fetch('origin')
|
317
|
+
diff = git('diff', src).stdout
|
318
|
+
return unless diff && !diff.empty?
|
319
|
+
|
320
|
+
puts "Will merge the following suggestions:\n\n#{diff}"
|
321
|
+
|
322
|
+
loop do
|
323
|
+
$stdout.print("\nAre you sure? [y/n] ")
|
324
|
+
ans = $stdin.gets.strip
|
325
|
+
case ans
|
326
|
+
when /^[Yy]$/
|
327
|
+
system(which('git'), 'merge', '--ff', "origin/#{current_branch}")
|
328
|
+
break
|
329
|
+
when /^[Nn]$/, /^[Qq](uit)?/
|
330
|
+
puts 'Not merging at user request...'
|
331
|
+
break
|
332
|
+
else
|
333
|
+
puts "Didn't understand '#{ans}'."
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
alias ps pullsuggestions
|
339
|
+
|
268
340
|
private
|
269
341
|
|
270
342
|
def _smartpush(remote, branch, force)
|
@@ -302,11 +374,11 @@ class SugarJar
|
|
302
374
|
|
303
375
|
args = ['push', remote, branch]
|
304
376
|
args << '--force-with-lease' if force
|
305
|
-
puts
|
377
|
+
puts git(*args).stderr
|
306
378
|
end
|
307
379
|
|
308
380
|
def dirty?
|
309
|
-
s =
|
381
|
+
s = git_nofail('diff', '--quiet')
|
310
382
|
s.error?
|
311
383
|
end
|
312
384
|
|
@@ -343,9 +415,9 @@ class SugarJar
|
|
343
415
|
end
|
344
416
|
|
345
417
|
def set_hub_host
|
346
|
-
return unless in_repo
|
418
|
+
return unless hub? && in_repo && @ghhost
|
347
419
|
|
348
|
-
s =
|
420
|
+
s = git_nofail('config', '--local', '--get', 'hub.host')
|
349
421
|
if s.error?
|
350
422
|
SugarJar::Log.info("Setting repo hub.host = #{@ghhost}")
|
351
423
|
else
|
@@ -363,7 +435,7 @@ class SugarJar
|
|
363
435
|
end
|
364
436
|
return
|
365
437
|
end
|
366
|
-
|
438
|
+
git('config', '--local', '--add', 'hub.host', @ghhost)
|
367
439
|
end
|
368
440
|
|
369
441
|
def set_commit_template
|
@@ -384,7 +456,7 @@ class SugarJar
|
|
384
456
|
)
|
385
457
|
end
|
386
458
|
|
387
|
-
s =
|
459
|
+
s = git_nofail('config', '--local', 'commit.template')
|
388
460
|
unless s.error?
|
389
461
|
current = s.stdout.strip
|
390
462
|
if current == @repo_config['commit_template']
|
@@ -402,18 +474,60 @@ class SugarJar
|
|
402
474
|
'Setting repo-specific commit template to ' +
|
403
475
|
"#{@repo_config['commit_template']} per sugarjar repo config.",
|
404
476
|
)
|
405
|
-
|
477
|
+
git(
|
406
478
|
'config', '--local', 'commit.template', @repo_config['commit_template']
|
407
479
|
)
|
408
480
|
end
|
409
481
|
|
410
|
-
def
|
411
|
-
unless @repo_config[type]
|
412
|
-
|
413
|
-
|
482
|
+
def get_checks_from_command(type)
|
483
|
+
return nil unless @repo_config["#{type}_list_cmd"]
|
484
|
+
|
485
|
+
cmd = @repo_config["#{type}_list_cmd"]
|
486
|
+
short = cmd.split.first
|
487
|
+
unless File.exist?(short)
|
488
|
+
SugarJar::Log.error(
|
489
|
+
"Configured #{type}_list_cmd #{short} does not exist!",
|
490
|
+
)
|
491
|
+
return false
|
492
|
+
end
|
493
|
+
s = Mixlib::ShellOut.new(cmd).run_command
|
494
|
+
if s.error?
|
495
|
+
SugarJar::Log.error(
|
496
|
+
"#{type}_list_cmd (#{cmd}) failed: #{s.format_for_exception}",
|
497
|
+
)
|
498
|
+
return false
|
499
|
+
end
|
500
|
+
s.stdout.split("\n")
|
501
|
+
end
|
502
|
+
|
503
|
+
# determine if we're using the _list_cmd and if so run it to get the
|
504
|
+
# checks, or just use the directly-defined check, and cache it
|
505
|
+
def get_checks(type)
|
506
|
+
return @checks[type] if @checks[type]
|
507
|
+
|
508
|
+
ret = get_checks_from_command(type)
|
509
|
+
if ret
|
510
|
+
SugarJar::Log.debug("Found #{type}s: #{ret}")
|
511
|
+
@checks[type] = ret
|
512
|
+
# if it's explicitly false, we failed to run the command
|
513
|
+
elsif ret == false
|
514
|
+
@checks[type] = false
|
515
|
+
# otherwise, we move on (basically: it's nil, there was no _list_cmd)
|
516
|
+
else
|
517
|
+
SugarJar::Log.debug("[#{type}]: using listed linters: #{ret}")
|
518
|
+
@checks[type] = @repo_config[type] || []
|
414
519
|
end
|
520
|
+
@checks[type]
|
521
|
+
end
|
522
|
+
|
523
|
+
def run_check(type)
|
415
524
|
Dir.chdir repo_root do
|
416
|
-
|
525
|
+
checks = get_checks(type)
|
526
|
+
# if we failed to determine the checks, the the checks have effectively
|
527
|
+
# failed
|
528
|
+
return false unless checks
|
529
|
+
|
530
|
+
checks.each do |check|
|
417
531
|
SugarJar::Log.debug("Running #{type} #{check}")
|
418
532
|
|
419
533
|
short = check.split.first
|
@@ -431,7 +545,7 @@ class SugarJar
|
|
431
545
|
SugarJar::Log.warn(
|
432
546
|
"The linter modified the repo. Here's the diff:\n",
|
433
547
|
)
|
434
|
-
puts
|
548
|
+
puts git('diff').stdout
|
435
549
|
loop do
|
436
550
|
$stdout.print(
|
437
551
|
"\nWould you like to\n\t[q]uit and inspect\n\t[a]mend the " +
|
@@ -483,16 +597,53 @@ class SugarJar
|
|
483
597
|
exit(1)
|
484
598
|
end
|
485
599
|
|
600
|
+
def assert_common_main_branch
|
601
|
+
upstream_branch = main_remote_branch(upstream)
|
602
|
+
unless main_branch == upstream_branch
|
603
|
+
die(
|
604
|
+
"The local main branch is '#{main_branch}', but the main branch " +
|
605
|
+
"of the #{upstream} remote is '#{upstream_branch}'. You probably " +
|
606
|
+
"want to rename your local branch by doing:\n\t" +
|
607
|
+
"git branch -m #{main_branch} #{upstream_branch}\n\t" +
|
608
|
+
"git fetch #{upstream}\n\t" +
|
609
|
+
"git branch -u #{upstream}/#{upstream_branch} #{upstream_branch}\n" +
|
610
|
+
"\tgit remote set-head #{upstream} -a",
|
611
|
+
)
|
612
|
+
end
|
613
|
+
return if upstream_branch == 'origin'
|
614
|
+
|
615
|
+
origin_branch = main_remote_branch('origin')
|
616
|
+
return if origin_branch == upstream_branch
|
617
|
+
|
618
|
+
die(
|
619
|
+
"The main branch of your upstream (#{upstream_branch}) and your " +
|
620
|
+
"fork/origin (#{origin_branch}) are not the same. You should go " +
|
621
|
+
"to https://#{@ghhost || 'github.com'}/#{@ghuser}/#{repo_name}/" +
|
622
|
+
'branches/ and rename the \'default\' branch to ' +
|
623
|
+
"'#{upstream_branch}'. It will then give you some commands to " +
|
624
|
+
'run to update this clone.',
|
625
|
+
)
|
626
|
+
end
|
627
|
+
|
486
628
|
def assert_in_repo
|
487
629
|
die('sugarjar must be run from inside a git repo') unless in_repo
|
488
630
|
end
|
489
631
|
|
632
|
+
def determine_main_branch(branches)
|
633
|
+
branches.include?('main') ? 'main' : 'master'
|
634
|
+
end
|
635
|
+
|
490
636
|
def main_branch
|
491
|
-
@main_branch =
|
637
|
+
@main_branch = determine_main_branch(all_local_branches)
|
638
|
+
end
|
639
|
+
|
640
|
+
def main_remote_branch(remote)
|
641
|
+
@main_remote_branches[remote] ||=
|
642
|
+
determine_main_branch(all_remote_branches(remote))
|
492
643
|
end
|
493
644
|
|
494
645
|
def checkout_main_branch
|
495
|
-
|
646
|
+
git('checkout', main_branch)
|
496
647
|
end
|
497
648
|
|
498
649
|
def clean_branch(name)
|
@@ -503,15 +654,25 @@ class SugarJar
|
|
503
654
|
|
504
655
|
SugarJar::Log.debug('branch deemed safe to delete...')
|
505
656
|
checkout_main_branch
|
506
|
-
|
657
|
+
git('branch', '-D', name)
|
507
658
|
gitup
|
508
659
|
true
|
509
660
|
end
|
510
661
|
|
511
|
-
def
|
662
|
+
def all_remote_branches(remote = 'origin')
|
512
663
|
branches = []
|
513
|
-
|
514
|
-
|
664
|
+
git('branch', '-r', '--format', '%(refname)').stdout.lines.each do |line|
|
665
|
+
next unless line.start_with?("refs/remotes/#{remote}/")
|
666
|
+
|
667
|
+
branches << branch_from_ref(line.strip, :remote)
|
668
|
+
end
|
669
|
+
branches
|
670
|
+
end
|
671
|
+
|
672
|
+
def all_local_branches
|
673
|
+
branches = []
|
674
|
+
git('branch', '--format', '%(refname)').stdout.lines.each do |line|
|
675
|
+
branches << branch_from_ref(line.strip)
|
515
676
|
end
|
516
677
|
branches
|
517
678
|
end
|
@@ -520,7 +681,7 @@ class SugarJar
|
|
520
681
|
# cherry -v will output 1 line per commit on the target branch
|
521
682
|
# prefixed by a - or + - anything with a - can be dropped, anything
|
522
683
|
# else cannot.
|
523
|
-
out =
|
684
|
+
out = git(
|
524
685
|
'cherry', '-v', tracked_branch, branch
|
525
686
|
).stdout.lines.reject do |line|
|
526
687
|
line.start_with?('-')
|
@@ -539,8 +700,8 @@ class SugarJar
|
|
539
700
|
# First we need a temp branch to work on
|
540
701
|
tmpbranch = "_sugar_jar.#{Process.pid}"
|
541
702
|
|
542
|
-
|
543
|
-
s =
|
703
|
+
git('checkout', '-b', tmpbranch, tracked_branch)
|
704
|
+
s = git_nofail('merge', '--squash', branch)
|
544
705
|
if s.error?
|
545
706
|
cleanup_tmp_branch(tmpbranch, branch)
|
546
707
|
SugarJar::Log.debug(
|
@@ -551,7 +712,7 @@ class SugarJar
|
|
551
712
|
return false
|
552
713
|
end
|
553
714
|
|
554
|
-
s =
|
715
|
+
s = git('diff', '--staged')
|
555
716
|
out = s.stdout
|
556
717
|
SugarJar::Log.debug("Squash-merged diff: #{out}")
|
557
718
|
cleanup_tmp_branch(tmpbranch, branch)
|
@@ -569,18 +730,22 @@ class SugarJar
|
|
569
730
|
end
|
570
731
|
|
571
732
|
def cleanup_tmp_branch(tmp, backto)
|
572
|
-
|
573
|
-
|
574
|
-
|
733
|
+
git('reset', '--hard', tracked_branch)
|
734
|
+
git('checkout', backto)
|
735
|
+
git('branch', '-D', tmp)
|
575
736
|
end
|
576
737
|
|
577
738
|
def current_branch
|
578
|
-
|
739
|
+
branch_from_ref(git('symbolic-ref', 'HEAD').stdout.strip)
|
579
740
|
end
|
580
741
|
|
581
742
|
def fetch_upstream
|
582
743
|
us = upstream
|
583
|
-
|
744
|
+
fetch(us) if us
|
745
|
+
end
|
746
|
+
|
747
|
+
def fetch(remote)
|
748
|
+
git('fetch', remote)
|
584
749
|
end
|
585
750
|
|
586
751
|
def gitup
|
@@ -598,7 +763,7 @@ class SugarJar
|
|
598
763
|
)
|
599
764
|
end
|
600
765
|
SugarJar::Log.debug('Rebasing')
|
601
|
-
s =
|
766
|
+
s = git_nofail('rebase', base)
|
602
767
|
{
|
603
768
|
'so' => s,
|
604
769
|
'base' => base,
|
@@ -606,7 +771,7 @@ class SugarJar
|
|
606
771
|
end
|
607
772
|
|
608
773
|
def tracked_branch
|
609
|
-
s =
|
774
|
+
s = git_nofail(
|
610
775
|
'rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'
|
611
776
|
)
|
612
777
|
if s.error?
|
@@ -628,7 +793,7 @@ class SugarJar
|
|
628
793
|
def upstream
|
629
794
|
return @remote if @remote
|
630
795
|
|
631
|
-
s =
|
796
|
+
s = git('remote')
|
632
797
|
|
633
798
|
remotes = s.stdout.lines.map(&:strip)
|
634
799
|
SugarJar::Log.debug("remotes is #{remotes}")
|
@@ -646,6 +811,13 @@ class SugarJar
|
|
646
811
|
@remote
|
647
812
|
end
|
648
813
|
|
814
|
+
def branch_from_ref(ref, type = :local)
|
815
|
+
# local branches are refs/head/XXXX
|
816
|
+
# remote branches are refs/remotes/<remote>/XXXX
|
817
|
+
base = type == :local ? 2 : 3
|
818
|
+
ref.split('/')[base..].join('/')
|
819
|
+
end
|
820
|
+
|
649
821
|
def color(string, *colors)
|
650
822
|
if @color
|
651
823
|
pastel.decorate(string, *colors)
|
@@ -660,5 +832,21 @@ class SugarJar
|
|
660
832
|
Pastel.new
|
661
833
|
end
|
662
834
|
end
|
835
|
+
|
836
|
+
def hub?
|
837
|
+
@cli == 'hub'
|
838
|
+
end
|
839
|
+
|
840
|
+
def gh?
|
841
|
+
@cli == 'gh'
|
842
|
+
end
|
843
|
+
|
844
|
+
def ghcli_nofail(*args)
|
845
|
+
gh? ? gh_nofail(*args) : hub_nofail(*args)
|
846
|
+
end
|
847
|
+
|
848
|
+
def ghcli(*args)
|
849
|
+
gh? ? gh(*args) : hub(*args)
|
850
|
+
end
|
663
851
|
end
|
664
852
|
end
|
data/lib/sugarjar/config.rb
CHANGED
@@ -6,14 +6,15 @@ class SugarJar
|
|
6
6
|
# This is stuff like log level, github-user, etc.
|
7
7
|
class Config
|
8
8
|
DEFAULTS = {
|
9
|
-
'
|
9
|
+
'github_cli' => 'hub',
|
10
|
+
'github_user' => ENV.fetch('USER'),
|
10
11
|
'fallthru' => true,
|
11
12
|
}.freeze
|
12
13
|
|
13
14
|
def self._find_ordered_files
|
14
15
|
[
|
15
16
|
'/etc/sugarjar/config.yaml',
|
16
|
-
"#{
|
17
|
+
"#{Dir.home}/.config/sugarjar/config.yaml",
|
17
18
|
].select { |f| File.exist?(f) }
|
18
19
|
end
|
19
20
|
|
data/lib/sugarjar/util.rb
CHANGED
@@ -26,11 +26,25 @@ class SugarJar
|
|
26
26
|
exit(1)
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
29
|
+
def git_nofail(*args)
|
30
30
|
if %w{diff log grep branch}.include?(args[0]) &&
|
31
31
|
args.none? { |x| x.include?('color') }
|
32
32
|
args << (@color ? '--color' : '--no-color')
|
33
33
|
end
|
34
|
+
SugarJar::Log.trace("Running: git #{args.join(' ')}")
|
35
|
+
Mixlib::ShellOut.new([which('git')] + args).run_command
|
36
|
+
end
|
37
|
+
|
38
|
+
def git(*args)
|
39
|
+
s = git_nofail(*args)
|
40
|
+
s.error!
|
41
|
+
s
|
42
|
+
end
|
43
|
+
|
44
|
+
def hub_nofail(*args)
|
45
|
+
# this allows us to use 'hub' stuff that's top-level, but is under
|
46
|
+
# repo for this.
|
47
|
+
args.delete_at(0) if args[0] == 'repo'
|
34
48
|
SugarJar::Log.trace("Running: hub #{args.join(' ')}")
|
35
49
|
s = Mixlib::ShellOut.new([which('hub')] + args).run_command
|
36
50
|
if s.error?
|
@@ -84,13 +98,43 @@ class SugarJar
|
|
84
98
|
s
|
85
99
|
end
|
86
100
|
|
101
|
+
def gh_nofail(*args)
|
102
|
+
SugarJar::Log.trace("Running: gh #{args.join(' ')}")
|
103
|
+
s = Mixlib::ShellOut.new([which('gh')] + args).run_command
|
104
|
+
if s.error? && s.stderr.include?('gh auth')
|
105
|
+
SugarJar::Log.info(
|
106
|
+
'gh was run but no github token exists. Will run "gh auth login" ' +
|
107
|
+
"to force\ngh to authenticate...",
|
108
|
+
)
|
109
|
+
unless system(which('gh'), 'auth', 'login', '-p', 'ssh')
|
110
|
+
SugarJar::Log.fatal(
|
111
|
+
'That failed, I will bail out. Hub needs to get a github ' +
|
112
|
+
'token. Try running "gh auth login" (will list info about ' +
|
113
|
+
'your account) and try this again when that works.',
|
114
|
+
)
|
115
|
+
exit(1)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
s
|
119
|
+
end
|
120
|
+
|
121
|
+
def gh(*args)
|
122
|
+
s = gh_nofail(*args)
|
123
|
+
s.error!
|
124
|
+
s
|
125
|
+
end
|
126
|
+
|
87
127
|
def in_repo
|
88
|
-
s =
|
128
|
+
s = git_nofail('rev-parse', '--is-inside-work-tree')
|
89
129
|
!s.error? && s.stdout.strip == 'true'
|
90
130
|
end
|
91
131
|
|
92
132
|
def repo_root
|
93
|
-
|
133
|
+
git('rev-parse', '--show-toplevel').stdout.strip
|
134
|
+
end
|
135
|
+
|
136
|
+
def repo_name
|
137
|
+
repo_root.split('/').last
|
94
138
|
end
|
95
139
|
end
|
96
140
|
end
|
data/lib/sugarjar/version.rb
CHANGED
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: 0.0.
|
4
|
+
version: 0.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Phil Dibowitz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mixlib-log
|
@@ -95,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
requirements: []
|
98
|
-
rubygems_version: 3.3.
|
98
|
+
rubygems_version: 3.3.7
|
99
99
|
signing_key:
|
100
100
|
specification_version: 4
|
101
101
|
summary: A git/github helper script
|