sugarjar 0.0.1 → 0.0.6
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/Gemfile +10 -0
- data/README.md +232 -61
- data/bin/sj +239 -0
- data/lib/sugarjar/commands.rb +93 -19
- data/lib/sugarjar/config.rb +4 -2
- data/lib/sugarjar/util.rb +43 -1
- data/lib/sugarjar/version.rb +1 -1
- data/sugarjar.gemspec +21 -0
- metadata +10 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2820df2090fc51cb383edb67d4f4f450d659bed13da44cdf34c6fc9242084a10
|
4
|
+
data.tar.gz: 6571f45c6609aa80441cea9102da98f85fef8056bfe1e7bce1d566a6ce6e1bb3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 953d1400363c4ddddf92fb4c0f0cbbe239c60b0c19c9a47726e99c8f8bca48008079b903e9c921886a26dfe0b4c0d4495d89ae15cd32a298cfa7b5bf78317bc5
|
7
|
+
data.tar.gz: bea7b4b10e735797c288b3bece8ddad975f0ebc35f264e732bdda2d583d9380f719b7f471d1df1640f1bcd6e76a3d39e36456fa18d8ede04a10de0c40355784a
|
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
#
|
1
|
+
# SugarJar
|
2
2
|
|
3
3
|

|
4
|
+
[](https://badge.fury.io/rb/sugarjar)
|
4
5
|
|
5
6
|
Welcome to SugarJar - a git/github helper. It leverages the amazing GitHub cli,
|
6
7
|
[hub](https://hub.github.com/), so you'll need that installed.
|
@@ -12,84 +13,219 @@ the Phabricator workflow this aims to bring to the GitHub workflow.
|
|
12
13
|
In particular there are a lot of helpers for using a squash-merge workflow that
|
13
14
|
is poorly handled by the standard toolsets.
|
14
15
|
|
15
|
-
|
16
|
+
If you miss Mondrian or Phabrictor - this is the tool for you!
|
16
17
|
|
17
|
-
|
18
|
+
If you don't, there's a ton of useful stuff for everyone!
|
18
19
|
|
19
|
-
|
20
|
-
arguments such as `-a` or files.
|
20
|
+
## Auto cleanup squash-merged branches
|
21
21
|
|
22
|
-
|
22
|
+
It is common for a PR to go back and forth with a variety of nits, lint fixes,
|
23
|
+
typos, etc. that can muddy history. So many projects will "squash and merge"
|
24
|
+
when they accept a pull request. Howevet, that means `git branch -d <branch>`
|
25
|
+
doesn't work. Git will tell you the branch isn't fully merged. You can, of
|
26
|
+
course `git branch -D <branch>`, but that does no safety checks at all, it
|
27
|
+
forces the deletion.
|
23
28
|
|
24
|
-
|
25
|
-
|
29
|
+
Enter `sj bclean` - it determines of the contents of your branch has been merge
|
30
|
+
and safely deletes if so.
|
26
31
|
|
27
|
-
|
32
|
+
``` shell
|
33
|
+
sj bclean
|
34
|
+
```
|
28
35
|
|
29
|
-
|
30
|
-
squash-merged branches. Think of it as a smarter `git branch -d`.
|
36
|
+
Will delete a branch, if it has been merged, **even if it was squash-merged**.
|
31
37
|
|
32
|
-
|
38
|
+
You can pass it a branch if you'd like (it defaults to the branch you're on):
|
39
|
+
`sj bclean <branch>`.
|
33
40
|
|
34
|
-
|
35
|
-
|
41
|
+
But it gets better! You can use `sj bcleanall` to remove all branches that have
|
42
|
+
been merged:
|
36
43
|
|
37
|
-
|
44
|
+
```shell
|
45
|
+
$ git branch
|
46
|
+
* argparse
|
47
|
+
master
|
48
|
+
feature
|
49
|
+
hubhost
|
50
|
+
$ git bcleanall
|
51
|
+
Skipping branch argparse - there are unmerged commits
|
52
|
+
Reaped branch feature
|
53
|
+
Reaped branch hubhost
|
54
|
+
```
|
38
55
|
|
39
|
-
|
56
|
+
## Smarter clones and remotes
|
40
57
|
|
41
|
-
|
58
|
+
There's a pattern to every new repo we want to contribute to. First we fork,
|
59
|
+
then we clone the fork, then we add a remote of the upstream repo. It's
|
60
|
+
monotonous. SugarJar does this for you:
|
42
61
|
|
43
|
-
|
62
|
+
```shell
|
63
|
+
sj smartclone jaymzh/sugarjar
|
64
|
+
```
|
44
65
|
|
45
|
-
|
66
|
+
(also `sj sclone`)
|
46
67
|
|
47
|
-
|
48
|
-
it defaults to creating it based on some form of 'master' instead of your
|
49
|
-
current branch. In order of preference it will be `upstream/master`,
|
50
|
-
`origin/master`, or `master`, depending upon what remotes are available.
|
68
|
+
This will:
|
51
69
|
|
52
|
-
|
70
|
+
* Make a fork of the repo, if you don't already have one
|
71
|
+
* Clone your fork
|
72
|
+
* Add the original as an 'upstream' remote
|
53
73
|
|
54
|
-
|
55
|
-
|
56
|
-
amending. Never do this to shared branches. Very convenient for keeping the
|
57
|
-
branch behind a pull-request clean.
|
74
|
+
Note that it takes `hub`s short-names for repos. No need to specify a full URL,
|
75
|
+
just a $org/$repo.
|
58
76
|
|
59
|
-
|
77
|
+
Like `git clone`, `sj sclone` will accept an additional arguement as the
|
78
|
+
destination directory to clone to. It will also pass any other unknown options
|
79
|
+
to `git clone` under the hood.
|
60
80
|
|
61
|
-
|
81
|
+
## Work with stacked branches more easily
|
62
82
|
|
63
|
-
|
83
|
+
It's important to break changes into reviewable chunks, but working with
|
84
|
+
stacked branches can be confusing. Enter `binfo` - it gives you a view of your
|
85
|
+
current branch all the way up to master. In this example imagine we have a
|
86
|
+
branch structure like:
|
64
87
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
88
|
+
```text
|
89
|
+
+- test2.1
|
90
|
+
/
|
91
|
+
master --- test --- test2 --- test3
|
92
|
+
```
|
70
93
|
|
71
|
-
|
94
|
+
This is what `binfo` on test3 looks like:
|
72
95
|
|
73
|
-
|
74
|
-
|
96
|
+
```shell
|
97
|
+
$ sj binfo
|
98
|
+
* e451865 (HEAD -> test3) test3
|
99
|
+
* e545b41 (test2) test2
|
100
|
+
* c808eae (test1) test1
|
101
|
+
o 44cf9e2 (origin/master, origin/HEAD, master) Lint/gemspec cleanups
|
102
|
+
```
|
75
103
|
|
76
|
-
|
77
|
-
`origin` and whatever your current local branch name is.
|
104
|
+
while `binfo` on test2.1 looks like:
|
78
105
|
|
79
|
-
|
106
|
+
```shell
|
107
|
+
$ sj binfo
|
108
|
+
* 36d0136 (HEAD -> test2.1) test2.1
|
109
|
+
* e545b41 (test2) test2
|
110
|
+
* c808eae (test1) test1
|
111
|
+
o 44cf9e2 (origin/master, origin/HEAD, master) Lint/gemspec cleanups
|
112
|
+
```
|
80
113
|
|
81
|
-
|
114
|
+
## Have a better lint/unittest experience!
|
82
115
|
|
83
|
-
|
116
|
+
Ever made a PR, only to find out later that it failed tests because of some
|
117
|
+
small lint issue? Not anymore! SJ can be configured to run things before
|
118
|
+
pushing. For example,in the SugarJar repo, we have it run Rubocop (ruby lint)
|
119
|
+
and Markdownlint "on_push". If those fail, it lets you know and doesn't push.
|
84
120
|
|
85
|
-
|
86
|
-
|
121
|
+
You can configure SugarJar to tell how how to run both lints and unittests for
|
122
|
+
a given repo and if one or both should be run prior to pushing.
|
87
123
|
|
88
|
-
|
124
|
+
The details on the config file format is below, but we provide three commands:
|
89
125
|
|
90
|
-
|
126
|
+
```shell
|
127
|
+
git lint
|
128
|
+
```
|
91
129
|
|
92
|
-
|
130
|
+
Run all linters.
|
131
|
+
|
132
|
+
```shell
|
133
|
+
git unit
|
134
|
+
```
|
135
|
+
|
136
|
+
Run all unittests.
|
137
|
+
|
138
|
+
```shell
|
139
|
+
git smartpush # or spush
|
140
|
+
```
|
141
|
+
|
142
|
+
Run configured push-time actions (nothing, lint, unit, both), and do not
|
143
|
+
push if any of them fail.
|
144
|
+
|
145
|
+
## Better push defaults
|
146
|
+
|
147
|
+
In addition to running pre-push tests for you `smartpush` also picks smart
|
148
|
+
defaults for push. So if you `sj spush` with no arguements, it uses the
|
149
|
+
`origin` remote and the same branch name you're on as the remote branch.
|
150
|
+
|
151
|
+
## Cleaning up your own history
|
152
|
+
|
153
|
+
Perhaps you contribute to a project that prefers to use merge commits, so you
|
154
|
+
like to clean up your own history. This is often difficult to get right - a
|
155
|
+
combination of rebases, amends and force pushes. We provide two commands here
|
156
|
+
to help.
|
157
|
+
|
158
|
+
The first is pretty straight forward and is basically just an alias: `sj
|
159
|
+
amend`. It will ammend whatever you want to the most recent commit (just an
|
160
|
+
alias for `git commit --amend`). It has a partner `qamend` (or `amendq` if you
|
161
|
+
prefer) that will do so without prompting to update your commit message.
|
162
|
+
|
163
|
+
So now you've rebased or amended, pushing becomes challening. You can `git push
|
164
|
+
--force`, but everyone knows that's incredibly dangerous. Is there a better
|
165
|
+
way? There is! Git provides `git push --force-with-lease` - it checks to make
|
166
|
+
sure you're up-to-date with the remote before forcing the push. But man that
|
167
|
+
command is a mouthful! Enter `sj fpush`. It has all the smarts of `sj
|
168
|
+
smartpush` (runs configured pre-push actions), but adds `--force-with-lease` to
|
169
|
+
the command!
|
170
|
+
|
171
|
+
## Better feature branches
|
172
|
+
|
173
|
+
When you want to start a new feature, you want to start developing against
|
174
|
+
latest. That's why `sj feature` defaults to creating a branch against what we
|
175
|
+
call "most master". That is, `upstream/master` if it exists, otherwise
|
176
|
+
`origin/master` if that exists, otherwise `master`. You can pass in an
|
177
|
+
additional arguement to base it off of something else.
|
178
|
+
|
179
|
+
```shell
|
180
|
+
$ git branch
|
181
|
+
master
|
182
|
+
test1
|
183
|
+
test2
|
184
|
+
* test2.1
|
185
|
+
test3
|
186
|
+
$ sj feature test-branch
|
187
|
+
Created feature branch test-branch based on origin/master
|
188
|
+
$ sj feature dependent-feature test-branch
|
189
|
+
Created feature branch dependent-feature based on test-branch
|
190
|
+
```
|
191
|
+
|
192
|
+
## And more!
|
193
|
+
|
194
|
+
See `sj help` for more commands!
|
195
|
+
|
196
|
+
## Using SugarJar as a git wrapper
|
197
|
+
|
198
|
+
SugarJar, by default, will pass any command it doesn't know straight to `hub`
|
199
|
+
(which passes commands **it** doesn't know to `git`). As such you can alias it
|
200
|
+
to `git` and just have a super-git.
|
201
|
+
|
202
|
+
```shell
|
203
|
+
$ alias git=sj
|
204
|
+
$ git config -l | grep color
|
205
|
+
color.diff=auto
|
206
|
+
color.status=auto
|
207
|
+
color.branch=auto
|
208
|
+
color.branch.current=yellow reverse
|
209
|
+
color.branch.local=yellow
|
210
|
+
color.branch.remote=green
|
211
|
+
$ git br
|
212
|
+
* dependent-feature 44cf9e2 Lint/gemspec cleanups
|
213
|
+
master 44cf9e2 Lint/gemspec cleanups
|
214
|
+
test-branch 44cf9e2 Lint/gemspec cleanups
|
215
|
+
test1 c808eae [ahead 1] test1
|
216
|
+
test2 e545b41 test2
|
217
|
+
test2.1 c1831b3 test2.1
|
218
|
+
test3 e451865 test3
|
219
|
+
```
|
220
|
+
|
221
|
+
It's for this reason that SugarJar doesn't have conflicting command names. You
|
222
|
+
can turn off fallthru by setting `fallthru: false` in your config.
|
223
|
+
|
224
|
+
The only command we "override" is `version`, in which case we not only print
|
225
|
+
our version, but also call `hub version` which prints its version and calls
|
226
|
+
`git version` too!
|
227
|
+
|
228
|
+
## Configuration
|
93
229
|
|
94
230
|
Sugarjar will read in both a system-level config file
|
95
231
|
(`/etc/sugarjar/config.yaml`) and a user-level config file
|
@@ -110,17 +246,19 @@ troubleshoot configuration parsing.
|
|
110
246
|
## Repository Configuration
|
111
247
|
|
112
248
|
Sugarjar looks for a `.sugarjar.yaml` in the root of the repository to tell it
|
113
|
-
how to handle repo-specific things. Currently there are
|
114
|
-
configurations accepted:
|
249
|
+
how to handle repo-specific things. Currently there options are:
|
115
250
|
|
116
|
-
* lint - A list of scripts to run on `sj lint`. These should be linters like
|
251
|
+
* `lint` - A list of scripts to run on `sj lint`. These should be linters like
|
117
252
|
rubocop or pyflake.
|
118
|
-
* unit - A list of scripts to run on `sj unit`. These should be unittest
|
253
|
+
* `unit` - A list of scripts to run on `sj unit`. These should be unittest
|
119
254
|
runners like rspec or pyunit.
|
120
|
-
* on_push - A list of types (`lint`, `unit`) of checks to run before pushing.
|
255
|
+
* `on_push` - A list of types (`lint`, `unit`) of checks to run before pushing.
|
121
256
|
It is highly recommended this is only `lint`. The goal here is to allow for
|
122
257
|
the user to get quick stylistic feedback before pushing their branch to avoid
|
123
258
|
the push-fix-push-fix loop.
|
259
|
+
* `commit_template` - A path to a commit template to set in the `commit.template`
|
260
|
+
git config for this repo. Should be either a fully-qualified path, or a path
|
261
|
+
relative to the repo root.
|
124
262
|
|
125
263
|
Example configuration:
|
126
264
|
|
@@ -131,19 +269,52 @@ unit:
|
|
131
269
|
- scripts/unit
|
132
270
|
on_push:
|
133
271
|
- lint
|
272
|
+
commit_template: .commit-template.txt
|
273
|
+
```
|
274
|
+
|
275
|
+
### Commit Templates
|
276
|
+
|
277
|
+
While GitHub provides a way to specify a pull-request template by putting the
|
278
|
+
right file into a repo, there is no way to tell git to automatically pick up a
|
279
|
+
commit template by dropping a file in the repo. Users must do something like:
|
280
|
+
`git config commit.template <file>`. Making each developer do this is error
|
281
|
+
prone, so this setting will automatically set this up for each developer.
|
282
|
+
|
283
|
+
## Enterprise GitHub
|
284
|
+
|
285
|
+
Like `hub`, SugarJar supports GitHub Enterprise. In fact, we provide extra
|
286
|
+
features just for it.
|
287
|
+
|
288
|
+
We recommend the global or user config specify the `github_host`. However, most
|
289
|
+
users will also have a few repos from upstream so always specifying a
|
290
|
+
`github_host` is sub-optimal.
|
291
|
+
|
292
|
+
So, when you overwrite the `github_host` on the command line, we go ahead and
|
293
|
+
set the `hub.host` git config in that single repo so that it'll "just work"
|
294
|
+
from there on out.
|
295
|
+
|
296
|
+
In other words, assuming your global SJ config has `github_host:
|
297
|
+
github.sample.com`, and the you clone sugarjar with:
|
298
|
+
|
299
|
+
```shell
|
300
|
+
sj clone jaymzh/sugarjar --github-host githuh.com
|
134
301
|
```
|
135
302
|
|
303
|
+
We will add the `hub.host` to the `sugarjar` clone so that future `hub` or `sj`
|
304
|
+
commands work without needing to specify..
|
305
|
+
|
136
306
|
## FAQ
|
137
307
|
|
138
308
|
Why the name SugarJar?
|
139
309
|
|
140
|
-
It's mostly a backranym. Like jellyfish, I wanted two letters that were on
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
310
|
+
It's mostly a backranym. Like jellyfish, I wanted two letters that were on home
|
311
|
+
row on different sides of the keyboard to make it easy to type. I looked at the
|
312
|
+
possible options that where there and not taken and tried to find one I could
|
313
|
+
make an appropriate name out of. Since this utility adds lots of sugar to git
|
314
|
+
and github, it seemed appropriate.
|
145
315
|
|
146
316
|
Why did you use `hub` instead of the newer `gh` CLI?
|
147
317
|
|
148
|
-
`gh` is
|
149
|
-
|
318
|
+
`gh` is still new and not yet as feature rich as `hub`. Also I wanted SugarJar
|
319
|
+
to be able to be a git wrapper, and so wrapping `hub` allows us to do that but
|
320
|
+
wrapping `gh` does not.
|
data/bin/sj
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# SugarJar
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
require 'mixlib/shellout'
|
6
|
+
require_relative '../lib/sugarjar/commands'
|
7
|
+
require_relative '../lib/sugarjar/config'
|
8
|
+
require_relative '../lib/sugarjar/log'
|
9
|
+
require_relative '../lib/sugarjar/util'
|
10
|
+
require_relative '../lib/sugarjar/version'
|
11
|
+
|
12
|
+
SugarJar::Log.level = Logger::INFO
|
13
|
+
|
14
|
+
# Don't put defaults here, put them in SugarJar::Config - otherwise
|
15
|
+
# these defaults overwrite whatever is in config files.
|
16
|
+
options = {}
|
17
|
+
# If ENV['SUGARJAR_DEBUG'] is set, it overrides the config file,
|
18
|
+
# but not the command line options, so set that one here. Also
|
19
|
+
# start the logger at that level, in case we are debugging option loading
|
20
|
+
# itself
|
21
|
+
if ENV['SUGARJAR_LOGLEVEL']
|
22
|
+
options['log_level'] = SugarJar::Log.level = ENV['SUGARJAR_LOGLEVEL'].to_sym
|
23
|
+
end
|
24
|
+
parser = OptionParser.new do |opts|
|
25
|
+
opts.banner = 'Usage: sj <command> [<args>] [<options>]'
|
26
|
+
|
27
|
+
opts.separator ''
|
28
|
+
opts.separator 'Command, args, and options, can appear in any order.'
|
29
|
+
opts.separator ''
|
30
|
+
opts.separator 'OPTIONS:'
|
31
|
+
|
32
|
+
opts.on('--[no-]fallthru', 'Fall-thru to git') do |fallthru|
|
33
|
+
options['fallthru'] = fallthru
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on('--github-user USER', 'Github username') do |user|
|
37
|
+
options['github_user'] = user
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on(
|
41
|
+
'--github-host HOST',
|
42
|
+
'The host for "hub". Note that we will set this in the local repo ' +
|
43
|
+
'config so there is no need to have multiple config files for multiple ' +
|
44
|
+
'github servers. Put your default one in your config file, and simply ' +
|
45
|
+
'specify this option the first time you clone or touch a repo and it ' +
|
46
|
+
'will be part of that repo until changed.',
|
47
|
+
) do |host|
|
48
|
+
options['github_host'] = host
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('-h', '--help', 'Print this help message') do
|
52
|
+
puts opts
|
53
|
+
exit
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on(
|
57
|
+
'--log-level LEVEL',
|
58
|
+
'Set logging level (fatal, error, warning, info, debug, trace). Default: ' +
|
59
|
+
'info',
|
60
|
+
) do |level|
|
61
|
+
options['log_level'] = level
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on('--version') do
|
65
|
+
puts SugarJar::VERSION
|
66
|
+
exit
|
67
|
+
end
|
68
|
+
|
69
|
+
# rubocop:disable Layout/HeredocIndentation
|
70
|
+
opts.separator <<COMMANDS
|
71
|
+
|
72
|
+
COMMANDS:
|
73
|
+
amend
|
74
|
+
Amend the current commit. Alias for "git commit --amend".
|
75
|
+
Accepts other arguments such as "-a" or files.
|
76
|
+
|
77
|
+
amendq, qamend
|
78
|
+
Same as "amend" but without changing the message. Alias for
|
79
|
+
"git commit --amend --no-edit".
|
80
|
+
|
81
|
+
bclean
|
82
|
+
If safe, delete the current branch. Unlike "git branch -d",
|
83
|
+
bclean can handle squash-merged branches. Think of it as
|
84
|
+
a smarter "git branch -d".
|
85
|
+
|
86
|
+
bcleanall
|
87
|
+
Walk all branches, and try to delete them if it's safe. See
|
88
|
+
"bclean" for details.
|
89
|
+
|
90
|
+
binfo
|
91
|
+
Verbose information about the current branch.
|
92
|
+
|
93
|
+
br
|
94
|
+
Verbose branch list. An alias for "git branch -v".
|
95
|
+
|
96
|
+
feature
|
97
|
+
Create a "feature" branch. It's morally equivalent to
|
98
|
+
"git checkout -b" except it defaults to creating it based on
|
99
|
+
some form of 'master' instead of your current branch. In order
|
100
|
+
of preference it will be upstream/master, origin/master, master,
|
101
|
+
depending upon what remotes are available.
|
102
|
+
|
103
|
+
forcepush, fpush
|
104
|
+
The same as "smartpush", but uses "--force-with-lease". This is
|
105
|
+
a "safer" way of doing force-pushes and is the recommended way
|
106
|
+
to push after rebasing or amending. Never do this to shared
|
107
|
+
branches. Very convenient for keeping the branch behind a pull-
|
108
|
+
request clean.
|
109
|
+
|
110
|
+
lint
|
111
|
+
Run any linters configured in .sugarjar.yaml.
|
112
|
+
|
113
|
+
smartclone, sclone
|
114
|
+
A smart wrapper to "git clone" that handles forking and managing
|
115
|
+
remotes for you.
|
116
|
+
It will clone a git repository using hub-style short name
|
117
|
+
("$org/$repo"). If the org of the repository is not the same
|
118
|
+
as your github-user then it will fork the repo for you to
|
119
|
+
your account (if not already done) and then setup your remotes
|
120
|
+
so that "origin" is your fork and "upstream" is the upstream.
|
121
|
+
|
122
|
+
smartpush, spush
|
123
|
+
A smart wrapper to "git push" that runs whatever is defined in
|
124
|
+
"on_push" in .sugarjar.yml, and only pushes if they succeed.
|
125
|
+
|
126
|
+
unit
|
127
|
+
Run any unitests configured in .sugarjar.yaml.
|
128
|
+
|
129
|
+
up
|
130
|
+
Rebase the current branch on upstream/master or origin/master.
|
131
|
+
|
132
|
+
upall
|
133
|
+
Same as "up", but for all branches.
|
134
|
+
|
135
|
+
verson
|
136
|
+
Print the version of sugarjar, and then run 'hub version'
|
137
|
+
to show the hub and git versions.
|
138
|
+
COMMANDS
|
139
|
+
|
140
|
+
# rubocop:enable Layout/HeredocIndentation
|
141
|
+
end
|
142
|
+
|
143
|
+
# we make a copy of these because we will assign back to the ARGV
|
144
|
+
# we parse later. We also need a pristine copy in case we want to
|
145
|
+
# run git as we were called.
|
146
|
+
argv_copy = ARGV.dup
|
147
|
+
|
148
|
+
# We don't have options yet, but we need an instance of SJ in order
|
149
|
+
# to list public methods. We will recreate it
|
150
|
+
sj = SugarJar::Commands.new(options.merge({ 'no_change' => true }))
|
151
|
+
extra_opts = []
|
152
|
+
|
153
|
+
# as with above, this can't go into 'options', until after we parse
|
154
|
+
# the command line args
|
155
|
+
config = SugarJar::Config.config
|
156
|
+
|
157
|
+
valid_commands = sj.public_methods - Object.public_methods
|
158
|
+
|
159
|
+
is_valid_command = ARGV.any? { |arg| valid_commands.include?(arg.to_s.to_sym) }
|
160
|
+
|
161
|
+
# if we're configured to fall thru and the subcommand isn't one
|
162
|
+
# we recognize, don't parse the options as they may be different
|
163
|
+
# than git's. For example `git config -l` - we error because we
|
164
|
+
# require an arguement to `-l`.
|
165
|
+
if config['fallthru'] && !is_valid_command
|
166
|
+
SugarJar::Log.debug(
|
167
|
+
'Skipping option parsing: fall-thru is set and we do not recognize ' +
|
168
|
+
'any subcommands',
|
169
|
+
)
|
170
|
+
else
|
171
|
+
# We want to allow people to pass in extra args to be passed to
|
172
|
+
# git commands, but OptionParser doesn't easily allow this. So we
|
173
|
+
# loop over it, catching exceptions.
|
174
|
+
begin
|
175
|
+
# HOWEVER, anytime it throws an exception, for some reason, it clears
|
176
|
+
# out all of ARGV, or whatever you passed to as ARGV.
|
177
|
+
#
|
178
|
+
# This not only prevents further parsing, but also means we lose
|
179
|
+
# any non-option arguements (like the subcommand!)
|
180
|
+
#
|
181
|
+
# So we save a copy, and if we throw an exception, save the option that
|
182
|
+
# caused it, remove that option from our copy, and then re-populate argv
|
183
|
+
# with what's left.
|
184
|
+
#
|
185
|
+
# By doing this we not only get to parse all the options properly and
|
186
|
+
# save unknown ones, but non-option arguements, which OptionParser
|
187
|
+
# normally leaves in ARGV stay in ARGV.
|
188
|
+
saved_argv = argv_copy.dup
|
189
|
+
parser.parse!(argv_copy)
|
190
|
+
rescue OptionParser::InvalidOption => e
|
191
|
+
SugarJar::Log.debug("Saving unknown argument #{e.args}")
|
192
|
+
extra_opts += e.args
|
193
|
+
|
194
|
+
# e.args is an array, but it's only ever one arguement per exception
|
195
|
+
saved_argv.delete(e.args.first)
|
196
|
+
argv_copy = saved_argv.dup
|
197
|
+
SugarJar::Log.debug(
|
198
|
+
"Continuing option parsing with remaining ARGV: #{argv_copy}",
|
199
|
+
)
|
200
|
+
retry
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
if ARGV.empty?
|
205
|
+
puts parser
|
206
|
+
exit
|
207
|
+
end
|
208
|
+
|
209
|
+
options = config.merge(options)
|
210
|
+
|
211
|
+
# Recreate SJ with all of our options
|
212
|
+
SugarJar::Log.level = options['log_level'].to_sym if options['log_level']
|
213
|
+
sj = SugarJar::Commands.new(options)
|
214
|
+
|
215
|
+
subcommand = argv_copy.reject { |x| x.start_with?('-') }.first
|
216
|
+
argv_copy.delete(subcommand)
|
217
|
+
SugarJar::Log.debug("subcommand is #{subcommand}")
|
218
|
+
|
219
|
+
# Extra options we got, plus any left over arguements are what we
|
220
|
+
# pass to Commands so they can be passed to git as necessary
|
221
|
+
extra_opts += argv_copy
|
222
|
+
SugarJar::Log.debug("extra unknown options: #{extra_opts}")
|
223
|
+
|
224
|
+
if subcommand == 'help'
|
225
|
+
puts parser
|
226
|
+
exit
|
227
|
+
end
|
228
|
+
|
229
|
+
if is_valid_command
|
230
|
+
SugarJar::Log.debug(
|
231
|
+
"running #{subcommand}; extra opts: #{extra_opts.join(', ')}",
|
232
|
+
)
|
233
|
+
sj.send(subcommand.to_sym, *extra_opts)
|
234
|
+
elsif options['fallthru']
|
235
|
+
SugarJar::Log.debug("Falling thru to: hub #{ARGV.join(' ')}")
|
236
|
+
exec('hub', *ARGV)
|
237
|
+
else
|
238
|
+
SugarJar::Log.error("No such subcommand: #{subcommand}")
|
239
|
+
end
|
data/lib/sugarjar/commands.rb
CHANGED
@@ -11,10 +11,14 @@ class SugarJar
|
|
11
11
|
include SugarJar::Util
|
12
12
|
|
13
13
|
def initialize(options)
|
14
|
+
SugarJar::Log.debug("Commands.initialize options: #{options}")
|
14
15
|
@ghuser = options['github_user']
|
15
16
|
@ghhost = options['github_host']
|
16
17
|
@repo_config = SugarJar::RepoConfig.config
|
18
|
+
return if options['no_change']
|
19
|
+
|
17
20
|
set_hub_host if @ghhost
|
21
|
+
set_commit_template if @repo_config['commit_template']
|
18
22
|
end
|
19
23
|
|
20
24
|
def feature(name, base = nil)
|
@@ -22,6 +26,8 @@ class SugarJar
|
|
22
26
|
SugarJar::Log.debug("Feature: #{name}, #{base}")
|
23
27
|
die("#{name} already exists!") if all_branches.include?(name)
|
24
28
|
base ||= most_master
|
29
|
+
base_pieces = base.split('/')
|
30
|
+
hub('fetch', base_pieces[0]) if base_pieces.length > 1
|
25
31
|
hub('checkout', '-b', name, base)
|
26
32
|
SugarJar::Log.info("Created feature branch #{name} based on #{base}")
|
27
33
|
end
|
@@ -38,22 +44,30 @@ class SugarJar
|
|
38
44
|
|
39
45
|
def bcleanall
|
40
46
|
assert_in_repo
|
47
|
+
curr = current_branch
|
41
48
|
all_branches.each do |branch|
|
42
49
|
next if branch == 'master'
|
43
50
|
|
44
51
|
# rubocop:disable Style/Next
|
45
52
|
unless clean_branch(branch)
|
46
53
|
SugarJar::Log.info(
|
47
|
-
"Skipping branch #{branch} - there are unmerged commits"
|
54
|
+
"Skipping branch #{branch} - there are unmerged commits",
|
48
55
|
)
|
49
56
|
end
|
50
57
|
# rubocop:enable Style/Next
|
51
58
|
end
|
59
|
+
|
60
|
+
# Return to the branch we were on, or master
|
61
|
+
if all_branches.include?(curr)
|
62
|
+
hub('checkout', curr)
|
63
|
+
else
|
64
|
+
hub('checkout', 'master')
|
65
|
+
end
|
52
66
|
end
|
53
67
|
|
54
|
-
def co(
|
68
|
+
def co(*args)
|
55
69
|
assert_in_repo
|
56
|
-
hub('checkout',
|
70
|
+
hub('checkout', *args)
|
57
71
|
end
|
58
72
|
|
59
73
|
def br
|
@@ -82,7 +96,7 @@ class SugarJar
|
|
82
96
|
def amend(*args)
|
83
97
|
assert_in_repo
|
84
98
|
# This cannot use shellout since we need a full terminal for the editor
|
85
|
-
exit(system('
|
99
|
+
exit(system(which('git'), 'commit', '--amend', *args))
|
86
100
|
end
|
87
101
|
|
88
102
|
def qamend(*args)
|
@@ -104,7 +118,7 @@ class SugarJar
|
|
104
118
|
else
|
105
119
|
SugarJar::Log.error(
|
106
120
|
"Failed to rebase #{branch}, aborting that and moving to next " +
|
107
|
-
'branch'
|
121
|
+
'branch',
|
108
122
|
)
|
109
123
|
hub('rebase', '--abort')
|
110
124
|
end
|
@@ -127,7 +141,7 @@ class SugarJar
|
|
127
141
|
|
128
142
|
org = File.basename(File.dirname(repo))
|
129
143
|
if org == @ghuser
|
130
|
-
|
144
|
+
puts 'Cloned forked or self-owned repo. Not creating "upstream".'
|
131
145
|
return
|
132
146
|
end
|
133
147
|
|
@@ -152,7 +166,7 @@ class SugarJar
|
|
152
166
|
end
|
153
167
|
|
154
168
|
def unit
|
155
|
-
exit(1) unless run_check('
|
169
|
+
exit(1) unless run_check('unit')
|
156
170
|
end
|
157
171
|
|
158
172
|
def smartpush(remote = nil, branch = nil)
|
@@ -202,14 +216,60 @@ class SugarJar
|
|
202
216
|
if current == @ghost
|
203
217
|
SugarJar::Log.debug('Repo hub.host already set correctly')
|
204
218
|
else
|
205
|
-
|
206
|
-
|
219
|
+
# Even though we have an explicit config, in most cases, it
|
220
|
+
# comes from a global or user config, but the config in the
|
221
|
+
# local repo we likely set. So we'd just constantly revert that.
|
222
|
+
SugarJar::Log.debug(
|
223
|
+
"Not overwriting repo hub.host. Already set to #{current}. " +
|
224
|
+
"To change it, run `git config --local --add hub.host #{@ghhost}`",
|
207
225
|
)
|
208
226
|
end
|
227
|
+
return
|
209
228
|
end
|
210
229
|
hub('config', '--local', '--add', 'hub.host', @ghhost)
|
211
230
|
end
|
212
231
|
|
232
|
+
def set_commit_template
|
233
|
+
unless in_repo
|
234
|
+
SugarJar::Log.debug('Skipping set_commit_template: not in repo')
|
235
|
+
return
|
236
|
+
end
|
237
|
+
|
238
|
+
realpath = if @repo_config['commit_template'].start_with?('/')
|
239
|
+
@repo_config['commit_template']
|
240
|
+
else
|
241
|
+
"#{repo_root}/#{@repo_config['commit_template']}"
|
242
|
+
end
|
243
|
+
unless File.exist?(realpath)
|
244
|
+
die(
|
245
|
+
"Repo config specifies #{@repo_config['commit_template']} as the " +
|
246
|
+
'commit template, but that file does not exist.',
|
247
|
+
)
|
248
|
+
end
|
249
|
+
|
250
|
+
s = hub_nofail('config', '--local', 'commit.template')
|
251
|
+
unless s.error?
|
252
|
+
current = s.stdout.strip
|
253
|
+
if current == @repo_config['commit_template']
|
254
|
+
SugarJar::Log.debug('Commit template already set correctly')
|
255
|
+
return
|
256
|
+
else
|
257
|
+
SugarJar::Log.warn(
|
258
|
+
"Updating repo-specific commit template from #{current} " +
|
259
|
+
"to #{@repo_config['commit_template']}",
|
260
|
+
)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
SugarJar::Log.debug(
|
265
|
+
'Setting repo-specific commit template to ' +
|
266
|
+
"#{@repo_config['commit_template']} per sugarjar repo config.",
|
267
|
+
)
|
268
|
+
hub(
|
269
|
+
'config', '--local', 'commit.template', @repo_config['commit_template']
|
270
|
+
)
|
271
|
+
end
|
272
|
+
|
213
273
|
def run_check(type)
|
214
274
|
unless @repo_config[type]
|
215
275
|
SugarJar::Log.debug("No #{type} configured. Returning success")
|
@@ -224,16 +284,20 @@ class SugarJar
|
|
224
284
|
return false
|
225
285
|
end
|
226
286
|
s = Mixlib::ShellOut.new(check).run_command
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
287
|
+
next unless s.error?
|
288
|
+
|
289
|
+
SugarJar::Log.info(
|
290
|
+
"#{type} #{check} failed, output follows (use debug for more)\n" +
|
291
|
+
s.stdout.to_s,
|
292
|
+
)
|
293
|
+
SugarJar::Log.debug(s.format_for_exception)
|
294
|
+
return false
|
231
295
|
end
|
232
296
|
end
|
233
297
|
end
|
234
298
|
|
235
299
|
def run_prepush
|
236
|
-
@repo_config['on_push']
|
300
|
+
@repo_config['on_push']&.each do |item|
|
237
301
|
SugarJar::Log.debug("Running on_push check type #{item}")
|
238
302
|
unless send(:run_check, item)
|
239
303
|
SugarJar::Log.info("Push check #{item} failed.")
|
@@ -287,7 +351,7 @@ class SugarJar
|
|
287
351
|
end
|
288
352
|
if out.length.zero?
|
289
353
|
SugarJar::Log.debug(
|
290
|
-
"cherry-pick shows branch #{branch} obviously safe to delete"
|
354
|
+
"cherry-pick shows branch #{branch} obviously safe to delete",
|
291
355
|
)
|
292
356
|
return true
|
293
357
|
end
|
@@ -303,10 +367,10 @@ class SugarJar
|
|
303
367
|
s = hub_nofail('merge', '--squash', branch)
|
304
368
|
if s.error?
|
305
369
|
cleanup_tmp_branch(tmpbranch, branch)
|
306
|
-
error(
|
370
|
+
SugarJar::Log.error(
|
307
371
|
'Failed to merge changes into current master. This means we could ' +
|
308
372
|
'not figure out if this is merged or not. Check manually and use ' +
|
309
|
-
"'git branch -D #{branch}' if it is safe to do so."
|
373
|
+
"'git branch -D #{branch}' if it is safe to do so.",
|
310
374
|
)
|
311
375
|
return false
|
312
376
|
end
|
@@ -317,12 +381,12 @@ class SugarJar
|
|
317
381
|
cleanup_tmp_branch(tmpbranch, branch)
|
318
382
|
if out.empty?
|
319
383
|
SugarJar::Log.debug(
|
320
|
-
'After squash-merging, this branch appears safe to delete'
|
384
|
+
'After squash-merging, this branch appears safe to delete',
|
321
385
|
)
|
322
386
|
true
|
323
387
|
else
|
324
388
|
SugarJar::Log.debug(
|
325
|
-
'After squash-merging, this branch is NOT fully merged to master'
|
389
|
+
'After squash-merging, this branch is NOT fully merged to master',
|
326
390
|
)
|
327
391
|
false
|
328
392
|
end
|
@@ -346,8 +410,18 @@ class SugarJar
|
|
346
410
|
def gitup
|
347
411
|
SugarJar::Log.debug('Fetching upstream')
|
348
412
|
fetch_upstream
|
413
|
+
curr = current_branch
|
349
414
|
SugarJar::Log.debug('Rebasing')
|
350
415
|
base = tracked_branch
|
416
|
+
if curr != 'master' && base == "origin/#{curr}"
|
417
|
+
SugarJar::Log.warn(
|
418
|
+
"This branch is tracking origin/#{curr}, which is probably your " +
|
419
|
+
'downstream (where you push _to_) as opposed to your upstream ' +
|
420
|
+
'(where you pull _from_). This means that "sj up" is probably ' +
|
421
|
+
'rebasing on the wrong thing and doing nothing. You probably want ' +
|
422
|
+
'to do a "git branch -u upstream".',
|
423
|
+
)
|
424
|
+
end
|
351
425
|
s = hub_nofail('rebase', base)
|
352
426
|
s.error? ? nil : base
|
353
427
|
end
|
data/lib/sugarjar/config.rb
CHANGED
@@ -6,7 +6,7 @@ class SugarJar
|
|
6
6
|
# This is stuff like log level, github-user, etc.
|
7
7
|
class Config
|
8
8
|
DEFAULTS = {
|
9
|
-
'
|
9
|
+
'github_user' => ENV['USER'],
|
10
10
|
'fallthru' => true,
|
11
11
|
}.freeze
|
12
12
|
|
@@ -22,7 +22,9 @@ class SugarJar
|
|
22
22
|
c = DEFAULTS.dup
|
23
23
|
_find_ordered_files.each do |f|
|
24
24
|
SugarJar::Log.debug("Loading config #{f}")
|
25
|
-
|
25
|
+
data = YAML.safe_load(File.read(f))
|
26
|
+
# an empty file is a `nil` which you can't merge
|
27
|
+
c.merge!(YAML.safe_load(File.read(f))) if data
|
26
28
|
SugarJar::Log.debug("Modified config: #{c}")
|
27
29
|
end
|
28
30
|
c
|
data/lib/sugarjar/util.rb
CHANGED
@@ -3,9 +3,51 @@ require_relative 'log'
|
|
3
3
|
class SugarJar
|
4
4
|
# Some common methods needed by other classes
|
5
5
|
module Util
|
6
|
+
# Finds the first entry in the path for a binary and checks
|
7
|
+
# to make sure it's not us (i.e. we may be linked to as 'git'
|
8
|
+
# or 'hub', but when we are calling that, we don't want ourselves.
|
9
|
+
def which_nofail(cmd)
|
10
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |dir|
|
11
|
+
p = File.join(dir, cmd)
|
12
|
+
# if it exists, and it is executable and is not us...
|
13
|
+
if File.exist?(p) && File.executable?(p) &&
|
14
|
+
File.basename(File.realpath(p)) != 'sj'
|
15
|
+
return p
|
16
|
+
end
|
17
|
+
end
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def which(cmd)
|
22
|
+
path = which_nofail(cmd)
|
23
|
+
return path if path
|
24
|
+
|
25
|
+
SugarJar::Log.fatal("Could not find #{cmd} in your path")
|
26
|
+
exit(1)
|
27
|
+
end
|
28
|
+
|
6
29
|
def hub_nofail(*args)
|
7
30
|
SugarJar::Log.trace("Running: hub #{args.join(' ')}")
|
8
|
-
Mixlib::ShellOut.new(['
|
31
|
+
s = Mixlib::ShellOut.new([which('hub')] + args).run_command
|
32
|
+
# depending on hub version and possibly other things, STDERR
|
33
|
+
# is either "Requires authentication" or "Must authenticate"
|
34
|
+
if s.error? && s.stderr =~ /^(Must|Requires) authenticat/
|
35
|
+
SugarJar::Log.info(
|
36
|
+
'Hub was run but no github token exists. Will run "hub api user" ' +
|
37
|
+
"to force\nhub to authenticate...",
|
38
|
+
)
|
39
|
+
unless system(which('hub'), 'api', 'user')
|
40
|
+
SugarJar::Log.fatal(
|
41
|
+
'That failed, I will bail out. Hub needs to get a github ' +
|
42
|
+
'token. Try running "hub api user" (will list info about ' +
|
43
|
+
'your account) and try this again when that works.',
|
44
|
+
)
|
45
|
+
exit(1)
|
46
|
+
end
|
47
|
+
SugarJar::Log.info('Re-running original hub command...')
|
48
|
+
s = Mixlib::ShellOut.new([which('hub')] + args).run_command
|
49
|
+
end
|
50
|
+
s
|
9
51
|
end
|
10
52
|
|
11
53
|
def hub(*args)
|
data/lib/sugarjar/version.rb
CHANGED
data/sugarjar.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'lib/sugarjar/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'sugarjar'
|
5
|
+
spec.version = SugarJar::VERSION
|
6
|
+
spec.summary = 'A git/github helper script'
|
7
|
+
spec.authors = ['Phil Dibowitz']
|
8
|
+
spec.email = ['phil@ipom.com']
|
9
|
+
spec.license = 'Apache-2.0'
|
10
|
+
spec.homepage = 'https://github.com/jaymzh/sugarjar'
|
11
|
+
docs = %w{README.md LICENSE Gemfile sugarjar.gemspec}
|
12
|
+
spec.extra_rdoc_files = docs
|
13
|
+
spec.executables << 'sj'
|
14
|
+
spec.files =
|
15
|
+
Dir.glob('lib/sugarjar/*.rb') +
|
16
|
+
Dir.glob('bin/*') +
|
17
|
+
docs
|
18
|
+
|
19
|
+
spec.add_dependency 'mixlib-log'
|
20
|
+
spec.add_dependency 'mixlib-shellout'
|
21
|
+
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: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Phil Dibowitz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06
|
11
|
+
date: 2020-07-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mixlib-log
|
@@ -38,79 +38,29 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: yaml
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: bundler
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: mdl
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rubocop
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
41
|
description:
|
98
42
|
email:
|
99
43
|
- phil@ipom.com
|
100
|
-
executables:
|
44
|
+
executables:
|
45
|
+
- sj
|
101
46
|
extensions: []
|
102
47
|
extra_rdoc_files:
|
103
48
|
- README.md
|
104
49
|
- LICENSE
|
50
|
+
- Gemfile
|
51
|
+
- sugarjar.gemspec
|
105
52
|
files:
|
53
|
+
- Gemfile
|
106
54
|
- LICENSE
|
107
55
|
- README.md
|
56
|
+
- bin/sj
|
108
57
|
- lib/sugarjar/commands.rb
|
109
58
|
- lib/sugarjar/config.rb
|
110
59
|
- lib/sugarjar/log.rb
|
111
60
|
- lib/sugarjar/repoconfig.rb
|
112
61
|
- lib/sugarjar/util.rb
|
113
62
|
- lib/sugarjar/version.rb
|
63
|
+
- sugarjar.gemspec
|
114
64
|
homepage: https://github.com/jaymzh/sugarjar
|
115
65
|
licenses:
|
116
66
|
- Apache-2.0
|
@@ -130,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
80
|
- !ruby/object:Gem::Version
|
131
81
|
version: '0'
|
132
82
|
requirements: []
|
133
|
-
rubygems_version: 3.
|
83
|
+
rubygems_version: 3.1.2
|
134
84
|
signing_key:
|
135
85
|
specification_version: 4
|
136
86
|
summary: A git/github helper script
|