sugarjar 0.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 +7 -0
- data/LICENSE +201 -0
- data/README.md +149 -0
- data/lib/sugarjar/commands.rb +396 -0
- data/lib/sugarjar/config.rb +31 -0
- data/lib/sugarjar/log.rb +24 -0
- data/lib/sugarjar/repoconfig.rb +32 -0
- data/lib/sugarjar/util.rb +26 -0
- data/lib/sugarjar/version.rb +3 -0
- metadata +137 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d2c4dd59aaf4843033f5c38df43b3fdfa556298708bc7f437f2923e00f9133c8
|
|
4
|
+
data.tar.gz: 5edeeb730db94059fd10b115b192ed239782b797473eb3b1fcf5aa749ca23e95
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 3869512549691df320f1dc344424b65944cd8cb501e25cd1a982f9b8d4d92cf608531147a8c5eb7a7437eee7644683ef9cf57e6035c44b61e4d247eec3331e03
|
|
7
|
+
data.tar.gz: 159e663e59a6c2aa939a5e9aecb90ce811bb9a168476d842ced28eab7a9968b760daec22661455a7bdaf3ac75ecc11a1c706e2ea07a6368ab653bcc3a222a597
|
data/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
data/README.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# SugjarJar
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Welcome to SugarJar - a git/github helper. It leverages the amazing GitHub cli,
|
|
6
|
+
[hub](https://hub.github.com/), so you'll need that installed.
|
|
7
|
+
|
|
8
|
+
SugarJar is inspired by [arcanist](https://github.com/phacility/arcanist), and
|
|
9
|
+
it's replacement at Facebook, JellyFish. Many of the features they provide for
|
|
10
|
+
the Phabricator workflow this aims to bring to the GitHub workflow.
|
|
11
|
+
|
|
12
|
+
In particular there are a lot of helpers for using a squash-merge workflow that
|
|
13
|
+
is poorly handled by the standard toolsets.
|
|
14
|
+
|
|
15
|
+
## Commands
|
|
16
|
+
|
|
17
|
+
### amend
|
|
18
|
+
|
|
19
|
+
Amend the current commit. Alias for `git commit --amend`. Accepts other
|
|
20
|
+
arguments such as `-a` or files.
|
|
21
|
+
|
|
22
|
+
### amendq, qamend
|
|
23
|
+
|
|
24
|
+
Same as `amend` but without changing the message. Alias for `git commit --amend
|
|
25
|
+
--no-edit`.
|
|
26
|
+
|
|
27
|
+
### bclean
|
|
28
|
+
|
|
29
|
+
If safe, delete the current branch. Unlike `git branch -d`, bclean can handle
|
|
30
|
+
squash-merged branches. Think of it as a smarter `git branch -d`.
|
|
31
|
+
|
|
32
|
+
### bcleanall
|
|
33
|
+
|
|
34
|
+
Walk all branches, and try to delete them if it's safe. See `bclean` for
|
|
35
|
+
details.
|
|
36
|
+
|
|
37
|
+
### binfo
|
|
38
|
+
|
|
39
|
+
Verbose information about the current branch.
|
|
40
|
+
|
|
41
|
+
### br
|
|
42
|
+
|
|
43
|
+
Verbose branch list. An alias for `git branch -v`.
|
|
44
|
+
|
|
45
|
+
### feature
|
|
46
|
+
|
|
47
|
+
Create a "feature branch." It's morally equivalent to `git checkout -b` except
|
|
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.
|
|
51
|
+
|
|
52
|
+
### forcepush, fpush
|
|
53
|
+
|
|
54
|
+
The same as `smartpush`, but uses `--force-with-lease`. This is a "safer" way
|
|
55
|
+
of doing force-pushes and is the recommended way to push after rebasing or
|
|
56
|
+
amending. Never do this to shared branches. Very convenient for keeping the
|
|
57
|
+
branch behind a pull-request clean.
|
|
58
|
+
|
|
59
|
+
### lint
|
|
60
|
+
|
|
61
|
+
Run any linters configured in `.sugarjar.yaml`.
|
|
62
|
+
|
|
63
|
+
### smartclone, sclone
|
|
64
|
+
|
|
65
|
+
A smart wrapper to `git clone` that handles forking and managing remotes for
|
|
66
|
+
you. It will clone a git repository using hub-style short name (`$org/$repo`).
|
|
67
|
+
If the org of the repository is not the same as your github-user then it will
|
|
68
|
+
fork the repo for you to your account (if not already done) and then setup your
|
|
69
|
+
remotes so that `origin` is your fork and `upstream` is the upstream.
|
|
70
|
+
|
|
71
|
+
### smartpush, spush
|
|
72
|
+
|
|
73
|
+
A smart wrapper to `git push` that runs whatever is defined in `on_push` in
|
|
74
|
+
`.sugarjar.yml`, and only pushes if they succeed.
|
|
75
|
+
|
|
76
|
+
It will also allow you to not specify a remote or branch, and will default to
|
|
77
|
+
`origin` and whatever your current local branch name is.
|
|
78
|
+
|
|
79
|
+
### unit
|
|
80
|
+
|
|
81
|
+
Run any unitests configured in `.sugarjar.yaml`.
|
|
82
|
+
|
|
83
|
+
### up
|
|
84
|
+
|
|
85
|
+
Rebase the current branch on the branch it's tracking, or if it's tracking one
|
|
86
|
+
then, otherise `upstream/master` if it exists, or `origin/master`.
|
|
87
|
+
|
|
88
|
+
### upall
|
|
89
|
+
|
|
90
|
+
Same as `up`, but for all branches.
|
|
91
|
+
|
|
92
|
+
## User Configuration
|
|
93
|
+
|
|
94
|
+
Sugarjar will read in both a system-level config file
|
|
95
|
+
(`/etc/sugarjar/config.yaml`) and a user-level config file
|
|
96
|
+
`~/.config/sugarjar/config.yaml`, if they exist. Anything in the user config
|
|
97
|
+
will override the system config, and command-line options override both. The
|
|
98
|
+
yaml file is a straight key-value pair of options without their '--'. For
|
|
99
|
+
example:
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
debug: true
|
|
103
|
+
github-user: jaymzh
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
In addition, the environment variable `SUGARJAR_DEBUG` can be defined to set
|
|
107
|
+
debug on. This is primarily used as a way to turn debug on earlier in order to
|
|
108
|
+
troubleshoot configuration parsing.
|
|
109
|
+
|
|
110
|
+
## Repository Configuration
|
|
111
|
+
|
|
112
|
+
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 only three
|
|
114
|
+
configurations accepted:
|
|
115
|
+
|
|
116
|
+
* lint - A list of scripts to run on `sj lint`. These should be linters like
|
|
117
|
+
rubocop or pyflake.
|
|
118
|
+
* unit - A list of scripts to run on `sj unit`. These should be unittest
|
|
119
|
+
runners like rspec or pyunit.
|
|
120
|
+
* on_push - A list of types (`lint`, `unit`) of checks to run before pushing.
|
|
121
|
+
It is highly recommended this is only `lint`. The goal here is to allow for
|
|
122
|
+
the user to get quick stylistic feedback before pushing their branch to avoid
|
|
123
|
+
the push-fix-push-fix loop.
|
|
124
|
+
|
|
125
|
+
Example configuration:
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
lint:
|
|
129
|
+
- scripts/lint
|
|
130
|
+
unit:
|
|
131
|
+
- scripts/unit
|
|
132
|
+
on_push:
|
|
133
|
+
- lint
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## FAQ
|
|
137
|
+
|
|
138
|
+
Why the name SugarJar?
|
|
139
|
+
|
|
140
|
+
It's mostly a backranym. Like jellyfish, I wanted two letters that were on
|
|
141
|
+
home row on different sides of the keyboard to make it easy to type. I looked
|
|
142
|
+
at the possible options that where there and not taken and tried to find one
|
|
143
|
+
I could make an appropriate name out of. Since this utility adds lots of sugar
|
|
144
|
+
to git and github, it seemed appropriate.
|
|
145
|
+
|
|
146
|
+
Why did you use `hub` instead of the newer `gh` CLI?
|
|
147
|
+
|
|
148
|
+
`gh` is less feature-rich (currently). I'm also considering making this optionally
|
|
149
|
+
act as a wrapper to `hub` the way `hub` can be a wrapper to `git`.
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
require_relative 'util'
|
|
2
|
+
require_relative 'repoconfig'
|
|
3
|
+
require_relative 'log'
|
|
4
|
+
require_relative 'version'
|
|
5
|
+
|
|
6
|
+
class SugarJar
|
|
7
|
+
# This is the workhorse of SugarJar. Short of #initialize, all other public
|
|
8
|
+
# methods are "commands". Anything in private is internal implementation
|
|
9
|
+
# details.
|
|
10
|
+
class Commands
|
|
11
|
+
include SugarJar::Util
|
|
12
|
+
|
|
13
|
+
def initialize(options)
|
|
14
|
+
@ghuser = options['github_user']
|
|
15
|
+
@ghhost = options['github_host']
|
|
16
|
+
@repo_config = SugarJar::RepoConfig.config
|
|
17
|
+
set_hub_host if @ghhost
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def feature(name, base = nil)
|
|
21
|
+
assert_in_repo
|
|
22
|
+
SugarJar::Log.debug("Feature: #{name}, #{base}")
|
|
23
|
+
die("#{name} already exists!") if all_branches.include?(name)
|
|
24
|
+
base ||= most_master
|
|
25
|
+
hub('checkout', '-b', name, base)
|
|
26
|
+
SugarJar::Log.info("Created feature branch #{name} based on #{base}")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def bclean(name = nil)
|
|
30
|
+
assert_in_repo
|
|
31
|
+
name ||= current_branch
|
|
32
|
+
# rubocop:disable Style/GuardClause
|
|
33
|
+
unless clean_branch(name)
|
|
34
|
+
die("Cannot clean #{name} - there are unmerged commits")
|
|
35
|
+
end
|
|
36
|
+
# rubocop:enable Style/GuardClause
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def bcleanall
|
|
40
|
+
assert_in_repo
|
|
41
|
+
all_branches.each do |branch|
|
|
42
|
+
next if branch == 'master'
|
|
43
|
+
|
|
44
|
+
# rubocop:disable Style/Next
|
|
45
|
+
unless clean_branch(branch)
|
|
46
|
+
SugarJar::Log.info(
|
|
47
|
+
"Skipping branch #{branch} - there are unmerged commits"
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
# rubocop:enable Style/Next
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def co(name)
|
|
55
|
+
assert_in_repo
|
|
56
|
+
hub('checkout', name)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def br
|
|
60
|
+
assert_in_repo
|
|
61
|
+
puts hub('branch', '-v').stdout
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def binfo
|
|
65
|
+
assert_in_repo
|
|
66
|
+
SugarJar::Log.info(hub(
|
|
67
|
+
'log', '--graph', '--oneline', '--decorate', '--boundary',
|
|
68
|
+
"#{tracked_branch}.."
|
|
69
|
+
).stdout)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def up
|
|
73
|
+
assert_in_repo
|
|
74
|
+
result = gitup
|
|
75
|
+
if result
|
|
76
|
+
SugarJar::Log.info("Rebased branch on #{result}")
|
|
77
|
+
else
|
|
78
|
+
die('Failed to rebase current branch')
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def amend(*args)
|
|
83
|
+
assert_in_repo
|
|
84
|
+
# This cannot use shellout since we need a full terminal for the editor
|
|
85
|
+
exit(system('/usr/bin/git', 'commit', '--amend', *args))
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def qamend(*args)
|
|
89
|
+
assert_in_repo
|
|
90
|
+
SugarJar::Log.info(hub('commit', '--amend', '--no-edit', *args).stdout)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
alias amendq qamend
|
|
94
|
+
|
|
95
|
+
def upall
|
|
96
|
+
assert_in_repo
|
|
97
|
+
all_branches.each do |branch|
|
|
98
|
+
next if branch == 'master'
|
|
99
|
+
|
|
100
|
+
hub('checkout', branch)
|
|
101
|
+
result = gitup
|
|
102
|
+
if result
|
|
103
|
+
SugarJar::Log.info("Rebased #{branch} on #{result}")
|
|
104
|
+
else
|
|
105
|
+
SugarJar::Log.error(
|
|
106
|
+
"Failed to rebase #{branch}, aborting that and moving to next " +
|
|
107
|
+
'branch'
|
|
108
|
+
)
|
|
109
|
+
hub('rebase', '--abort')
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def smartclone(repo, dir = nil, *args)
|
|
115
|
+
# If the user has specified a hub host, set the environment variable
|
|
116
|
+
# since we don't have a repo to configure yet
|
|
117
|
+
ENV['GITHUB_HOST'] = @ghhost if @ghhost
|
|
118
|
+
|
|
119
|
+
reponame = File.basename(repo, '.git')
|
|
120
|
+
dir ||= reponame
|
|
121
|
+
SugarJar::Log.info("Cloning #{reponame}...")
|
|
122
|
+
hub('clone', repo, dir, *args)
|
|
123
|
+
|
|
124
|
+
Dir.chdir dir do
|
|
125
|
+
# Now that we have a repo, if we have a hub host set it.
|
|
126
|
+
set_hub_host if @ghhost
|
|
127
|
+
|
|
128
|
+
org = File.basename(File.dirname(repo))
|
|
129
|
+
if org == @ghuser
|
|
130
|
+
put 'Cloned forked or self-owned repo. Not creating "upstream".'
|
|
131
|
+
return
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
s = hub_nofail('fork', '--remote-name=origin')
|
|
135
|
+
if s.error?
|
|
136
|
+
# if the fork command failed, we already have one, so we have
|
|
137
|
+
# to swap the remote names ourselves
|
|
138
|
+
SugarJar::Log.info("Fork (#{@ghuser}/#{reponame}) detected.")
|
|
139
|
+
hub('remote', 'rename', 'origin', 'upstream')
|
|
140
|
+
hub('remote', 'add', 'origin', repo.gsub("#{org}/", "#{@ghuser}/"))
|
|
141
|
+
else
|
|
142
|
+
SugarJar::Log.info("Forked #{reponame} to #{@ghuser}")
|
|
143
|
+
end
|
|
144
|
+
SugarJar::Log.info('Remotes "origin" and "upstream" configured.')
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
alias sclone smartclone
|
|
149
|
+
|
|
150
|
+
def lint
|
|
151
|
+
exit(1) unless run_check('lint')
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def unit
|
|
155
|
+
exit(1) unless run_check('lint')
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def smartpush(remote = nil, branch = nil)
|
|
159
|
+
unless remote && branch
|
|
160
|
+
remote ||= 'origin'
|
|
161
|
+
branch ||= current_branch
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
if run_prepush
|
|
165
|
+
puts hub('push', remote, branch).stderr
|
|
166
|
+
else
|
|
167
|
+
SugarJar::Log.error('Pre-push checks failed. Not pushing.')
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
alias spush smartpush
|
|
172
|
+
|
|
173
|
+
def forcepush(remote = nil, branch = nil)
|
|
174
|
+
unless remote && branch
|
|
175
|
+
remote ||= 'origin'
|
|
176
|
+
branch ||= current_branch
|
|
177
|
+
end
|
|
178
|
+
if run_prepush
|
|
179
|
+
puts hub('push', '--force-with-lease', remote, branch).stderr
|
|
180
|
+
else
|
|
181
|
+
SugarJar::Log.error('Pre-push checks failed. Not pushing.')
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
alias fpush forcepush
|
|
186
|
+
|
|
187
|
+
def version
|
|
188
|
+
puts "sugarjar version #{SugarJar::VERSION}"
|
|
189
|
+
puts hub('version').stdout
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
private
|
|
193
|
+
|
|
194
|
+
def set_hub_host
|
|
195
|
+
return unless in_repo
|
|
196
|
+
|
|
197
|
+
s = hub_nofail('config', '--local', '--get', 'hub.host')
|
|
198
|
+
if s.error?
|
|
199
|
+
SugarJar::Log.info("Setting repo hub.host = #{@ghhost}")
|
|
200
|
+
else
|
|
201
|
+
current = s.stdout
|
|
202
|
+
if current == @ghost
|
|
203
|
+
SugarJar::Log.debug('Repo hub.host already set correctly')
|
|
204
|
+
else
|
|
205
|
+
SugarJar::Log.info(
|
|
206
|
+
"Overwriting repo hub.host from #{current} to #{@ghhost}"
|
|
207
|
+
)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
hub('config', '--local', '--add', 'hub.host', @ghhost)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def run_check(type)
|
|
214
|
+
unless @repo_config[type]
|
|
215
|
+
SugarJar::Log.debug("No #{type} configured. Returning success")
|
|
216
|
+
return true
|
|
217
|
+
end
|
|
218
|
+
Dir.chdir repo_root do
|
|
219
|
+
@repo_config[type].each do |check|
|
|
220
|
+
SugarJar::Log.info("Running #{type} #{check}")
|
|
221
|
+
|
|
222
|
+
unless File.exist?(check)
|
|
223
|
+
SugarJar::Log.error("Configured #{type} #{check} does not exist!")
|
|
224
|
+
return false
|
|
225
|
+
end
|
|
226
|
+
s = Mixlib::ShellOut.new(check).run_command
|
|
227
|
+
if s.error?
|
|
228
|
+
SugarJar::Log.info("#{type} #{check} failed\n#{s.stdout}")
|
|
229
|
+
return false
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def run_prepush
|
|
236
|
+
@repo_config['on_push'].each do |item|
|
|
237
|
+
SugarJar::Log.debug("Running on_push check type #{item}")
|
|
238
|
+
unless send(:run_check, item)
|
|
239
|
+
SugarJar::Log.info("Push check #{item} failed.")
|
|
240
|
+
return false
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
true
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def die(msg)
|
|
247
|
+
SugarJar::Log.fatal(msg)
|
|
248
|
+
exit(1)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def assert_in_repo
|
|
252
|
+
die('sugarjar must be run from inside a git repo') unless in_repo
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def clean_branch(name)
|
|
256
|
+
die('Cannot remove master branch') if name == 'master'
|
|
257
|
+
SugarJar::Log.debug('Fetch relevant remote...')
|
|
258
|
+
fetch_upstream
|
|
259
|
+
return false unless safe_to_clean(name)
|
|
260
|
+
|
|
261
|
+
SugarJar::Log.debug('branch deemed safe to delete...')
|
|
262
|
+
hub('checkout', 'master')
|
|
263
|
+
hub('branch', '-D', name)
|
|
264
|
+
gitup
|
|
265
|
+
SugarJar::Log.info("Reaped branch #{name}")
|
|
266
|
+
true
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def all_branches
|
|
270
|
+
branches = []
|
|
271
|
+
hub('branch', '--format', '%(refname)').stdout.lines.each do |line|
|
|
272
|
+
next if line == 'master'
|
|
273
|
+
|
|
274
|
+
branches << line.strip.split('/')[2]
|
|
275
|
+
end
|
|
276
|
+
branches
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def safe_to_clean(branch)
|
|
280
|
+
# cherry -v will output 1 line per commit on the target branch
|
|
281
|
+
# prefixed by a - or + - anything with a - can be dropped, anything
|
|
282
|
+
# else cannot.
|
|
283
|
+
out = hub(
|
|
284
|
+
'cherry', '-v', tracked_branch, branch
|
|
285
|
+
).stdout.lines.reject do |line|
|
|
286
|
+
line.start_with?('-')
|
|
287
|
+
end
|
|
288
|
+
if out.length.zero?
|
|
289
|
+
SugarJar::Log.debug(
|
|
290
|
+
"cherry-pick shows branch #{branch} obviously safe to delete"
|
|
291
|
+
)
|
|
292
|
+
return true
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# if the "easy" check didn't work, it's probably because there
|
|
296
|
+
# was a squash-merge. To check for that we make our own squash
|
|
297
|
+
# merge to upstream/master and see if that has any delta
|
|
298
|
+
|
|
299
|
+
# First we need a temp branch to work on
|
|
300
|
+
tmpbranch = "_sugar_jar.#{Process.pid}"
|
|
301
|
+
|
|
302
|
+
hub('checkout', '-b', tmpbranch, tracked_branch)
|
|
303
|
+
s = hub_nofail('merge', '--squash', branch)
|
|
304
|
+
if s.error?
|
|
305
|
+
cleanup_tmp_branch(tmpbranch, branch)
|
|
306
|
+
error(
|
|
307
|
+
'Failed to merge changes into current master. This means we could ' +
|
|
308
|
+
'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."
|
|
310
|
+
)
|
|
311
|
+
return false
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
s = hub('diff', '--staged')
|
|
315
|
+
out = s.stdout
|
|
316
|
+
SugarJar::Log.debug("Squash-merged diff: #{out}")
|
|
317
|
+
cleanup_tmp_branch(tmpbranch, branch)
|
|
318
|
+
if out.empty?
|
|
319
|
+
SugarJar::Log.debug(
|
|
320
|
+
'After squash-merging, this branch appears safe to delete'
|
|
321
|
+
)
|
|
322
|
+
true
|
|
323
|
+
else
|
|
324
|
+
SugarJar::Log.debug(
|
|
325
|
+
'After squash-merging, this branch is NOT fully merged to master'
|
|
326
|
+
)
|
|
327
|
+
false
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def cleanup_tmp_branch(tmp, backto)
|
|
332
|
+
hub('reset', '--hard', tracked_branch)
|
|
333
|
+
hub('checkout', backto)
|
|
334
|
+
hub('branch', '-D', tmp)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def current_branch
|
|
338
|
+
hub('symbolic-ref', 'HEAD').stdout.strip.split('/')[2]
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def fetch_upstream
|
|
342
|
+
us = upstream
|
|
343
|
+
hub('fetch', us) if us
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def gitup
|
|
347
|
+
SugarJar::Log.debug('Fetching upstream')
|
|
348
|
+
fetch_upstream
|
|
349
|
+
SugarJar::Log.debug('Rebasing')
|
|
350
|
+
base = tracked_branch
|
|
351
|
+
s = hub_nofail('rebase', base)
|
|
352
|
+
s.error? ? nil : base
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def tracked_branch
|
|
356
|
+
s = hub_nofail(
|
|
357
|
+
'rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'
|
|
358
|
+
)
|
|
359
|
+
if s.error?
|
|
360
|
+
most_master
|
|
361
|
+
else
|
|
362
|
+
s.stdout.strip
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def most_master
|
|
367
|
+
us = upstream
|
|
368
|
+
if us
|
|
369
|
+
"#{us}/master"
|
|
370
|
+
else
|
|
371
|
+
master
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def upstream
|
|
376
|
+
return @remote if @remote
|
|
377
|
+
|
|
378
|
+
s = hub('remote')
|
|
379
|
+
|
|
380
|
+
remotes = s.stdout.lines.map(&:strip)
|
|
381
|
+
SugarJar::Log.debug("remotes is #{remotes}")
|
|
382
|
+
if remotes.empty?
|
|
383
|
+
@remote = nil
|
|
384
|
+
elsif remotes.length == 1
|
|
385
|
+
@remote = remotes[0]
|
|
386
|
+
elsif remotes.include?('upstream')
|
|
387
|
+
@remote = 'upstream'
|
|
388
|
+
elsif remotes.include?('origin')
|
|
389
|
+
@remote = 'origin'
|
|
390
|
+
else
|
|
391
|
+
raise 'Could not determine "upstream" remote to use...'
|
|
392
|
+
end
|
|
393
|
+
@remote
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require_relative 'log'
|
|
3
|
+
|
|
4
|
+
class SugarJar
|
|
5
|
+
# This parses SugarJar configs (not to be confused with repoconfigs).
|
|
6
|
+
# This is stuff like log level, github-user, etc.
|
|
7
|
+
class Config
|
|
8
|
+
DEFAULTS = {
|
|
9
|
+
'ghuser' => ENV['USER'],
|
|
10
|
+
'fallthru' => true,
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def self._find_ordered_files
|
|
14
|
+
[
|
|
15
|
+
'/etc/sugarjar/config.yaml',
|
|
16
|
+
"#{ENV['HOME']}/.config/sugarjar/config.yaml"
|
|
17
|
+
].select { |f| File.exist?(f) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.config
|
|
21
|
+
SugarJar::Log.debug("Defaults: #{DEFAULTS}")
|
|
22
|
+
c = DEFAULTS.dup
|
|
23
|
+
_find_ordered_files.each do |f|
|
|
24
|
+
SugarJar::Log.debug("Loading config #{f}")
|
|
25
|
+
c.merge!(YAML.safe_load(File.read(f)))
|
|
26
|
+
SugarJar::Log.debug("Modified config: #{c}")
|
|
27
|
+
end
|
|
28
|
+
c
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/sugarjar/log.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'mixlib/log'
|
|
2
|
+
|
|
3
|
+
module Mixlib
|
|
4
|
+
module Log
|
|
5
|
+
# A simple formatter so that 'info' is just like 'puts'
|
|
6
|
+
# but everything else gets a severity
|
|
7
|
+
class Formatter
|
|
8
|
+
def call(severity, _time, _progname, msg)
|
|
9
|
+
if severity == 'INFO'
|
|
10
|
+
"#{msg2str(msg)}\n"
|
|
11
|
+
else
|
|
12
|
+
"#{severity}: #{msg2str(msg)}\n"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class SugarJar
|
|
20
|
+
# Our singleton logger
|
|
21
|
+
class Log
|
|
22
|
+
extend Mixlib::Log
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require_relative 'util'
|
|
2
|
+
require_relative 'log'
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
class SugarJar
|
|
6
|
+
# This parses SugarJar repoconfigs (not to be confused with configs).
|
|
7
|
+
# This is lint/unit/on_push configs.
|
|
8
|
+
class RepoConfig
|
|
9
|
+
extend SugarJar::Util
|
|
10
|
+
|
|
11
|
+
CONFIG_NAME = '.sugarjar.yaml'.freeze
|
|
12
|
+
|
|
13
|
+
def self.repo_config
|
|
14
|
+
::File.join(repo_root, CONFIG_NAME)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.config
|
|
18
|
+
unless in_repo
|
|
19
|
+
SugarJar::Log.debug('Not in repo, skipping repoconfig load')
|
|
20
|
+
return {}
|
|
21
|
+
end
|
|
22
|
+
config = repo_config
|
|
23
|
+
if File.exist?(config)
|
|
24
|
+
SugarJar::Log.debug("Loading repo config: #{config}")
|
|
25
|
+
YAML.safe_load(File.read(repo_config))
|
|
26
|
+
else
|
|
27
|
+
SugarJar::Log.debug("No repo config (#{config}), returning empty hash")
|
|
28
|
+
{}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require_relative 'log'
|
|
2
|
+
|
|
3
|
+
class SugarJar
|
|
4
|
+
# Some common methods needed by other classes
|
|
5
|
+
module Util
|
|
6
|
+
def hub_nofail(*args)
|
|
7
|
+
SugarJar::Log.trace("Running: hub #{args.join(' ')}")
|
|
8
|
+
Mixlib::ShellOut.new(['/usr/bin/hub'] + args).run_command
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def hub(*args)
|
|
12
|
+
s = hub_nofail(*args)
|
|
13
|
+
s.error!
|
|
14
|
+
s
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def in_repo
|
|
18
|
+
s = hub_nofail('rev-parse', '--is-inside-work-tree')
|
|
19
|
+
!s.error? && s.stdout.strip == 'true'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def repo_root
|
|
23
|
+
hub('rev-parse', '--show-toplevel').stdout.strip
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: sugarjar
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Phil Dibowitz
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2020-06-05 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: mixlib-log
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: mixlib-shellout
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
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
|
+
description:
|
|
98
|
+
email:
|
|
99
|
+
- phil@ipom.com
|
|
100
|
+
executables: []
|
|
101
|
+
extensions: []
|
|
102
|
+
extra_rdoc_files:
|
|
103
|
+
- README.md
|
|
104
|
+
- LICENSE
|
|
105
|
+
files:
|
|
106
|
+
- LICENSE
|
|
107
|
+
- README.md
|
|
108
|
+
- lib/sugarjar/commands.rb
|
|
109
|
+
- lib/sugarjar/config.rb
|
|
110
|
+
- lib/sugarjar/log.rb
|
|
111
|
+
- lib/sugarjar/repoconfig.rb
|
|
112
|
+
- lib/sugarjar/util.rb
|
|
113
|
+
- lib/sugarjar/version.rb
|
|
114
|
+
homepage: https://github.com/jaymzh/sugarjar
|
|
115
|
+
licenses:
|
|
116
|
+
- Apache-2.0
|
|
117
|
+
metadata: {}
|
|
118
|
+
post_install_message:
|
|
119
|
+
rdoc_options: []
|
|
120
|
+
require_paths:
|
|
121
|
+
- lib
|
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
123
|
+
requirements:
|
|
124
|
+
- - ">="
|
|
125
|
+
- !ruby/object:Gem::Version
|
|
126
|
+
version: '0'
|
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
requirements: []
|
|
133
|
+
rubygems_version: 3.0.3
|
|
134
|
+
signing_key:
|
|
135
|
+
specification_version: 4
|
|
136
|
+
summary: A git/github helper script
|
|
137
|
+
test_files: []
|