sugarjar 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,313 @@
1
+ class SugarJar
2
+ module Help
3
+ # Keyed by the "primary" command (the longest/most-descriptive name -
4
+ # not necessarily the actual method name in SugarJar::Commands, though
5
+ # both work identically for dispatch). `aliases` are other, usually
6
+ # shorter, names that dispatch to the same command. `description` is
7
+ # the full text shown by `sj help <command>`.
8
+ COMMANDS = {
9
+ 'amend' => {
10
+ :aliases => [],
11
+ :usage => '',
12
+ :description => <<~DESC,
13
+ Amend the current commit. Alias for `git commit --amend`.
14
+ Accepts other arguments such as `-a` or files.
15
+ DESC
16
+ },
17
+ 'amendq' => {
18
+ :aliases => ['qamend'],
19
+ :usage => '',
20
+ :description => <<~DESC,
21
+ Same as `amend` but without changing the message. Alias for
22
+ `git commit --amend --no-edit`.
23
+ DESC
24
+ },
25
+ 'binfo' => {
26
+ :aliases => [],
27
+ :usage => '',
28
+ :description => 'Verbose information about the current branch.',
29
+ },
30
+ 'br' => {
31
+ :aliases => [],
32
+ :usage => '',
33
+ :description => 'Verbose branch list. An alias for `git branch -v`.',
34
+ },
35
+ 'debuginfo' => {
36
+ :aliases => [],
37
+ :usage => '',
38
+ :description => <<~DESC,
39
+ Prints out a bunch of version and config information useful for
40
+ including in bug reports.
41
+ DESC
42
+ },
43
+ 'feature' => {
44
+ :aliases => ['f'],
45
+ :usage => '<branch_name>',
46
+ :description => <<~DESC,
47
+ Create a "feature" branch. It's morally equivalent to
48
+ `git checkout -b ...` except it defaults to creating it based on
49
+ some form of the primary branch (`master`, `main`, etc.) instead of
50
+ your current branch. In order of preference it will be
51
+ `upstream/$PRIMARY`, `origin/$PRIMARY`, `$PRIMARY`,
52
+ depending upon what remotes are available.
53
+
54
+ Note that you can specify `--feature-prefix` (or add `feature_prefix`
55
+ to your config's 'host_configs' section) to have all features created
56
+ with a prefix. This is useful for branch-based workflows where
57
+ developers are expected to create branches names that, for example,
58
+ start with their username.
59
+ DESC
60
+ },
61
+ 'forcepush' => {
62
+ :aliases => ['fpush'],
63
+ :usage => '',
64
+ :description => <<~DESC,
65
+ The same as `smartpush`, but uses `--force-with-lease`. This is
66
+ a "safer" way of doing force-pushes and is the recommended way
67
+ to push after rebasing or amending. Never do this to shared
68
+ branches. Very convenient for keeping the branch behind a pull-
69
+ request clean.
70
+ DESC
71
+ },
72
+ 'forcesync' => {
73
+ :aliases => ['fsync'],
74
+ :usage => '',
75
+ :description => <<~DESC,
76
+ Similar to `sync`, but never tries to rebase, always does a
77
+ hard reset.
78
+ DESC
79
+ },
80
+ 'globalbranchclean' => {
81
+ :aliases => ['gbclean'],
82
+ :usage => '[<branch>] [<remote>]',
83
+ :description => <<~DESC,
84
+ WARNING: EXPERIMENTAL COMMAND.
85
+
86
+ Combination of `localbranchclean` and `remotebranchclean`. Cleans up
87
+ both local and remote branches safely. See those commands for
88
+ details.
89
+ DESC
90
+ },
91
+ 'globalbranchcleanall' => {
92
+ :aliases => ['gbcleanall'],
93
+ :usage => '[<remote>]',
94
+ :description => <<~DESC,
95
+ WARNING: EXPERIMENTAL COMMAND.
96
+
97
+ Safely clean all branches, both local and remote. See
98
+ `globalbranchclean` for details.
99
+ DESC
100
+ },
101
+ 'lint' => {
102
+ :aliases => [],
103
+ :usage => '',
104
+ :description => 'Run any linters configured in the repoconfig.',
105
+ },
106
+ 'localbranchclean' => {
107
+ :aliases => %w{lbclean bclean},
108
+ :usage => '[<branch>]',
109
+ :description => <<~DESC,
110
+ If safe, delete the current branch (or the specified branch).
111
+ Unlike `git branch -d`, this can handle squash-merged branches.
112
+ Think of it as a smarter `git branch -d`.
113
+
114
+ Aliased to `bclean` for backwards compatibility.
115
+ DESC
116
+ },
117
+ 'localbranchcleanall' => {
118
+ :aliases => %w{lbcleanall bcleanall},
119
+ :usage => '',
120
+ :description => <<~DESC,
121
+ Walk all branches, and try to delete them if it's safe. See
122
+ `localbranchclean` for details.
123
+
124
+ Aliased to `bcleanall` for backwards compatibility.
125
+ DESC
126
+ },
127
+ 'modernizeconfig' => {
128
+ :aliases => ['fixconfig'],
129
+ :usage => '',
130
+ :description => <<~DESC,
131
+ Will take a sugarjar config file and modernize it for all of
132
+ the changes in 4.x. You will be given a diff, and you can choose
133
+ to overwrite the file, or save the new one to a new file.
134
+ DESC
135
+ },
136
+ 'pullsuggestions' => {
137
+ :aliases => ['ps'],
138
+ :usage => '',
139
+ :description => <<~DESC,
140
+ Pull any suggestions *that have been committed* in the GitHub UI.
141
+ This will show the diff and prompt for confirmation before
142
+ merging. Note that a fast-forward merge will be used.
143
+ DESC
144
+ },
145
+ 'remotebranchclean' => {
146
+ :aliases => ['rbclean'],
147
+ :usage => '[<branch>] [<remote>]',
148
+ :description => <<~DESC,
149
+ WARNING: EXPERIMENTAL COMMAND.
150
+
151
+ Similar to `localbranchclean`, except safely cleans up remote
152
+ branches. Unlike many git commands, <remote> comes after <branch> so
153
+ that you can specify a branch and the remote defaults to `origin`.
154
+ This means you can do `sj remotebranchclean` to clean the remote
155
+ branch with the same name as the local one. Note that you probably
156
+ want `globalbranchclean`, which will do both local and remote cleaning
157
+ in one command.
158
+
159
+ WARNING: This command cannot differentiate release branches
160
+ that are fully merged but still need to be kept around for future
161
+ work unless they are specified in your repoconfig! So if main contains
162
+ everything that 2.0-devel and 3.0-devel has, then those branches will
163
+ be deleted. Use with caution.
164
+ DESC
165
+ },
166
+ 'remotebranchcleanall' => {
167
+ :aliases => ['rbcleanall'],
168
+ :usage => '[<remote>]',
169
+ :description => <<~DESC,
170
+ WARNING: EXPERIMENTAL COMMAND.
171
+
172
+ Walk all remote branches, and try to delete them if it's safe. See
173
+ `remotebranchclean` for details.
174
+ DESC
175
+ },
176
+ 'smartclone' => {
177
+ :aliases => ['sclone'],
178
+ :usage => '<repo> [<dir>]',
179
+ :description => <<~DESC,
180
+ A smart wrapper to `git clone` that handles forking and managing
181
+ remotes for you.
182
+
183
+ If the org of the repository is not the same as your forge user
184
+ then it will fork the repo for you to your account (if not
185
+ already done), clone the repo, and then setup your remotes
186
+ so that `origin` is your fork and `upstream` is the upstream.
187
+
188
+ It is assumed that there will be at least one positional
189
+ argument, and it will be the repo in any format (git, ssh,
190
+ forge-style shortname [e.g. e.g. `$org/$repo`]). A second
191
+ positional argument will be interpreted as the directory to
192
+ clone into.
193
+
194
+ If you want to change the name of the repo in your fork of it,
195
+ you may pass in `--fork-name` to specify another.
196
+
197
+ Note that if you pass in additional options after " -- ", they
198
+ will be passed to `gh` in the case of GitHub, or `git` in the
199
+ case of GitLab.
200
+
201
+ For example to clone foo/bar/docs on gitlab, but have the
202
+ repo named "bar-docs" when it's cloned to your org, and to
203
+ have the directory be called "bar-docs":
204
+
205
+ sj sclone foo/bar/docs bar-docs \\
206
+ --default-forge-host gitlab.com --fork-name bar-docs
207
+
208
+ Or for GitHub:
209
+
210
+ sj sclone bar/docs bar-docs \\
211
+ --default-forge-host github.com --fork-name bar-docs
212
+ DESC
213
+ },
214
+ 'smartlog' => {
215
+ :aliases => ['sl'],
216
+ :usage => '',
217
+ :description => <<~DESC,
218
+ Inspired by Facebook's `sl` extension to Mercurial, this command
219
+ will show you a tree of all your local branches relative to your
220
+ upstream.
221
+ DESC
222
+ },
223
+ 'smartpullrequest' => {
224
+ :aliases => %w{smartpr spr},
225
+ :usage => '',
226
+ :description => <<~DESC,
227
+ A smart wrapper to `gh pr`/`glab mr`/etc. that checks if your repo
228
+ is dirty before creating the pull request, handles intelligently
229
+ picking the base, fills in the PR body, etc.
230
+ DESC
231
+ },
232
+ 'smartpush' => {
233
+ :aliases => ['spush'],
234
+ :usage => '',
235
+ :description => <<~DESC,
236
+ A smart wrapper to `git push` that runs whatever is defined in
237
+ `on_push` in the repoconfig, and only pushes if they succeed.
238
+ DESC
239
+ },
240
+ 'subfeature' => {
241
+ :aliases => ['sf'],
242
+ :usage => '<feature>',
243
+ :description =>
244
+ 'An alias for `sj feature <feature> <current_branch>`.',
245
+ },
246
+ 'sync' => {
247
+ :aliases => [],
248
+ :usage => '',
249
+ :description => <<~DESC,
250
+ Similar to `up`, except instead of rebasing on a tracked branch
251
+ (usually `upstream` remote), rebases to wherever our remote push
252
+ target is (usually `origin` remote). Useful for syncing work
253
+ across different machines.
254
+
255
+ For example, if you do some work on feature `foo` on machine1 and
256
+ push to `origin/foo` (intending to eventually merge to
257
+ `upstream/main`), then on machine2, you pull that branch, do more
258
+ work, which you also push to `origin/foo`, then on machine1, you
259
+ can do `sj sync` to pull down the changes from `origin/foo`. If
260
+ you have local changes, that are not already on `origin/foo`,
261
+ those will be rebased on top of the changes from `origin/foo`.
262
+ DESC
263
+ },
264
+ 'unit' => {
265
+ :aliases => [],
266
+ :usage => '',
267
+ :description => 'Run any unitests configured in the repoconfig.',
268
+ },
269
+ 'up' => {
270
+ :aliases => [],
271
+ :usage => '[<branch>]',
272
+ :description => <<~DESC,
273
+ Rebase the current branch (or specified branch) intelligently.
274
+ In most causes this will check for a primary branch on
275
+ `upstream`, then `origin`. If a branch explicitly tracks something
276
+ else, then that will be used, instead.
277
+ DESC
278
+ },
279
+ 'upall' => {
280
+ :aliases => [],
281
+ :usage => '',
282
+ :description => 'Same as `up`, but for all branches.',
283
+ },
284
+ }.freeze
285
+
286
+ # Map of every valid command/alias name -> its canonical (primary) name.
287
+ ALIASES = COMMANDS.each_with_object({}) do |(name, info), map|
288
+ map[name] = name
289
+ info[:aliases].each { |a| map[a] = name }
290
+ end.freeze
291
+
292
+ def self.canonical(name)
293
+ ALIASES[name]
294
+ end
295
+
296
+ def self.summary_list
297
+ COMMANDS.sort.map do |name, info|
298
+ names = ([name] + info[:aliases]).join(', ')
299
+ " #{names}"
300
+ end.join("\n")
301
+ end
302
+
303
+ def self.command_help(name)
304
+ canonical_name = canonical(name)
305
+ return nil unless canonical_name
306
+
307
+ info = COMMANDS[canonical_name]
308
+ names = ([canonical_name] + info[:aliases]).join(', ')
309
+ usage = info[:usage].empty? ? '' : " #{info[:usage]}"
310
+ "#{names}#{usage}\n\n#{info[:description]}"
311
+ end
312
+ end
313
+ end
@@ -1,4 +1,4 @@
1
- require_relative 'util'
1
+ require_relative 'git'
2
2
  require_relative 'log'
3
3
  require 'yaml'
4
4
  require 'deep_merge'
@@ -10,7 +10,7 @@ class SugarJar
10
10
  CONFIG_NAME = '.sugarjar.yaml'.freeze
11
11
 
12
12
  def self.repo_config_path(config)
13
- ::File.join(SugarJar::Util.repo_root, config)
13
+ ::File.join(SugarJar::Git.repo_root, config)
14
14
  end
15
15
 
16
16
  def self.hash_from_file(config_file)
@@ -25,7 +25,7 @@ class SugarJar
25
25
 
26
26
  def self.config(config = CONFIG_NAME)
27
27
  data = {}
28
- unless SugarJar::Util.in_repo?
28
+ unless SugarJar::Git.in_repo?
29
29
  SugarJar::Log.debug('Not in repo, skipping repoconfig load')
30
30
  return data
31
31
  end
data/lib/sugarjar/util.rb CHANGED
@@ -30,70 +30,5 @@ class SugarJar
30
30
  end
31
31
  false
32
32
  end
33
-
34
- def self.git_nofail(*args, color: true)
35
- if %w{diff log grep branch}.include?(args[0]) &&
36
- args.none? { |x| x.include?('color') }
37
- args << (color ? '--color' : '--no-color')
38
- end
39
- SugarJar::Log.trace("Running: git #{args.join(' ')}")
40
- Mixlib::ShellOut.new([which('git')] + args).run_command
41
- end
42
-
43
- def self.git(*, color: true)
44
- s = git_nofail(*, :color => color)
45
- s.error!
46
- s
47
- end
48
-
49
- def self.ghcli_nofail(*)
50
- forge_nofail('gh', *)
51
- end
52
-
53
- def self.ghcli(*)
54
- s = ghcli_nofail(*)
55
- s.error!
56
- s
57
- end
58
-
59
- def self.glcli_nofail(*)
60
- forge_nofail('glab', *)
61
- end
62
-
63
- def self.glcli(*)
64
- s = glcli_nofail(*)
65
- s.error!
66
- s
67
- end
68
-
69
- def self.forge_nofail(cli, *args)
70
- SugarJar::Log.trace("Running: #{cli} #{args.join(' ')}")
71
- bin = which(cli)
72
- s = Mixlib::ShellOut.new([bin] + args).run_command
73
- if s.error? && s.stderr.include?("#{cli} auth")
74
- SugarJar::Log.info(
75
- 'glab was run but no gitlab token exists. Will run ' +
76
- '"glab auth login" to force\ngh to authenticate...',
77
- )
78
- unless system(bin, 'auth', 'login', '-p', 'ssh')
79
- SugarJar::Log.fatal(
80
- 'That failed, I will bail out. Hub needs to get a github ' +
81
- 'token. Try running "gh auth login" (will list info about ' +
82
- 'your account) and try this again when that works.',
83
- )
84
- exit(1)
85
- end
86
- end
87
- s
88
- end
89
-
90
- def self.in_repo?
91
- s = git_nofail('rev-parse', '--is-inside-work-tree')
92
- !s.error? && s.stdout.strip == 'true'
93
- end
94
-
95
- def self.repo_root
96
- git('rev-parse', '--show-toplevel').stdout.strip
97
- end
98
33
  end
99
34
  end
@@ -1,3 +1,3 @@
1
1
  class SugarJar
2
- VERSION = '3.0.0'.freeze
2
+ VERSION = '4.0.0'.freeze
3
3
  end
data/sugarjar.gemspec CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
26
26
  Dir.glob('extras/*')
27
27
 
28
28
  spec.add_dependency 'deep_merge'
29
+ spec.add_dependency 'diffy'
29
30
  spec.add_dependency 'mixlib-log'
30
31
  spec.add_dependency 'mixlib-shellout'
31
32
  spec.add_dependency 'pastel'
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: 3.0.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phil Dibowitz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-08 00:00:00.000000000 Z
11
+ date: 2026-08-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deep_merge
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: diffy
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'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: mixlib-log
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -98,12 +112,16 @@ files:
98
112
  - lib/sugarjar/commands/checks.rb
99
113
  - lib/sugarjar/commands/debuginfo.rb
100
114
  - lib/sugarjar/commands/feature.rb
115
+ - lib/sugarjar/commands/modernize_config.rb
101
116
  - lib/sugarjar/commands/pullsuggestions.rb
102
117
  - lib/sugarjar/commands/push.rb
103
118
  - lib/sugarjar/commands/smartclone.rb
104
119
  - lib/sugarjar/commands/smartpullrequest.rb
105
120
  - lib/sugarjar/commands/up.rb
106
121
  - lib/sugarjar/config.rb
122
+ - lib/sugarjar/forge.rb
123
+ - lib/sugarjar/git.rb
124
+ - lib/sugarjar/help.rb
107
125
  - lib/sugarjar/log.rb
108
126
  - lib/sugarjar/repoconfig.rb
109
127
  - lib/sugarjar/util.rb