sugarjar 2.0.2 → 3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0f9fa156720c59e1fc95f289273950176cad61ab82a67018d836a0ee878d81e
4
- data.tar.gz: 6bec27c224878e6faee0e5801dec631e39db36170b759b6bf335a045cdda75a0
3
+ metadata.gz: '0359bbf05d9c9930fb303ddcba57fd651d00de7ce046a5640c77f596fa148531'
4
+ data.tar.gz: 6a505ebcdc042fb4e3b6bd6c4e55e85401787f748a1f8874b4fd18e7489600e7
5
5
  SHA512:
6
- metadata.gz: e4a5e2900d551ce1bb4bc8f9899e37dff28d6faaa7c9a46e96c140febb3e8b240465499e491054525aed0c470e8892af2f8d3d546f8c19925c070d283df45478
7
- data.tar.gz: 979dce6e20a27fb94a11432fa5a39912693539de948f0a6278eb6662f68a6f62846e2d94667506094588592e99d5aba7fe57843e41771a55b128699a09595dce
6
+ metadata.gz: 88050a2cc342cac84d22ed6395f5fa36df8ff98361283408ae227611c4814b23881ae4a59eef276feb160bea04c9b84e81d6cd76d7eae0c8d8caac0e199dcdad
7
+ data.tar.gz: 932d4c9918a3eb50c078e6e3a7dd4728ea648cca222daa460954ec51ea7109fc374ea8a8517e9cf1b71d900ac5bb44c4a6120f0a7aaa581fee87d154a6be08f7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # SugarJar Changelog
2
2
 
3
+ ## 3.0.0 (2026-06-08)
4
+
5
+ * Add support for GitLab
6
+ * Add release-branch handling
7
+ * Fixes to `sync`/`fsync` to not leave repo in consistent state
8
+ * When in manual lint/unit, allow user to skip amending, and keep testing
9
+ * Fix typos in various messages
10
+
3
11
  ## 2.0.2 (2026-01-08)
4
12
 
5
13
  * Fix `branchclean` logic to properly compare with the target branch
data/README.md CHANGED
@@ -1,11 +1,21 @@
1
1
  # SugarJar
2
2
 
3
- [![Lint](https://github.com/jaymzh/sugarjar/workflows/Lint/badge.svg)](https://github.com/jaymzh/sugarjar/actions?query=workflow%3ALint)
4
- [![Unittest](https://github.com/jaymzh/sugarjar/workflows/Unittests/badge.svg)](https://github.com/jaymzh/sugarjar/actions?query=workflow%3AUnittests)
5
- [![DCO](https://github.com/jaymzh/sugarjar/workflows/DCO%20Check/badge.svg)](https://github.com/jaymzh/sugarjar/actions?query=workflow%3A%22DCO+Check%22)
6
-
7
- Welcome to SugarJar - a git/github helper. The only requirements are Ruby,
8
- `git`, and [gh](https://cli.github.com/).
3
+ [![Lint](
4
+ https://github.com/jaymzh/sugarjar/workflows/Lint/badge.svg
5
+ )](https://github.com/jaymzh/sugarjar/actions?query=workflow%3ALint)
6
+ [![Unittest](
7
+ https://github.com/jaymzh/sugarjar/workflows/Unittests/badge.svg
8
+ )](https://github.com/jaymzh/sugarjar/actions?query=workflow%3AUnittests)
9
+ [![DCO](
10
+ https://github.com/jaymzh/sugarjar/workflows/DCO%20Check/badge.svg
11
+ )](https://github.com/jaymzh/sugarjar/actions?query=workflow%3A%22DCO+Check%22)
12
+ [![CodeQL](
13
+ https://github.com/jaymzh/sugarjar/actions/workflows/codeql.yml/badge.svg
14
+ )](https://github.com/jaymzh/sugarjar/actions/workflows/codeql.yml)
15
+
16
+ Welcome to SugarJar - a git + github/gitlab helper. The only requirements are
17
+ Ruby, `git`, and either [gh](https://cli.github.com/) or
18
+ [glab](https://docs.gitlab.com/cli/), depending on which forge you are using.
9
19
 
10
20
  SugarJar is inspired by [arcanist](https://github.com/phacility/arcanist), and
11
21
  its replacement at Facebook, JellyFish. Many of the features they provide for
@@ -23,9 +33,14 @@ Jump to what you're most interested in:
23
33
  * [Common Use-cases](#common-use-cases)
24
34
  * [Auto Cleanup Squash-merged branches](#auto-cleanup-squash-merged-branches)
25
35
  * [Smarter clones and remotes](#smarter-clones-and-remotes)
26
- * [Work with stacked branches more easily](#work-with-stacked-branches-more-easily)
27
- * [Creating Stacked PRs with subfeatures](#creating-stacked-prs-with-subfeatures)
28
- * [Have a better lint/unittest experience!](#have-a-better-lintunittest-experience)
36
+ * [Work with stacked branches more easily](
37
+ #work-with-stacked-branches-more-easily)
38
+ * [Creating Stacked PRs with subfeatures](
39
+ #creating-stacked-prs-with-subfeatures)
40
+ * [Smart release branch handling](
41
+ #smart-release-branch-handling)
42
+ * [Have a better lint/unittest experience!](
43
+ #have-a-better-lintunittest-experience)
29
44
  * [Better push defaults](#better-push-defaults)
30
45
  * [Cleaning up your own history](#cleaning-up-your-own-history)
31
46
  * [Better feature branches](#better-feature-branches)
@@ -56,7 +71,9 @@ and safely deletes if so. (Note: `lbclean` stands for "local branch clean", and
56
71
  is aliased to `bclean` for both backwards-compatibility and also since it's the
57
72
  most common branch-cleanup command).
58
73
 
59
- ![bclean screenshot](https://github.com/jaymzh/sugarjar/blob/main/images/bclean.png)
74
+ ![bclean screenshot](
75
+ https://github.com/jaymzh/sugarjar/blob/main/images/bclean.png
76
+ )
60
77
 
61
78
  Will delete a branch, if it has been merged, **even if it was squash-merged**.
62
79
 
@@ -66,7 +83,12 @@ You can pass it a branch if you'd like (it defaults to the branch you're on):
66
83
  But it gets better! You can use `sj bcleanall` to remove all branches that have
67
84
  been merged:
68
85
 
69
- ![bcleanall screenshot](https://github.com/jaymzh/sugarjar/blob/main/images/bcleanall.png)
86
+ ![bcleanall screenshot](
87
+ https://github.com/jaymzh/sugarjar/blob/main/images/bcleanall.png
88
+ )
89
+
90
+ *NOTE*: You can add long-lived release-branches to your RepoConfig to prevent
91
+ cleaning; see [Smart release branch handling](️#smart-release-branch-handling).
70
92
 
71
93
  There is also `sj rbclean` ("remote branch clean") (and `sj rbcleanall`) for
72
94
  cleanup of remote branches. *Note*: This cannot differentiate between
@@ -74,8 +96,8 @@ PR/feature branches which have been merged and long-lived release branches that
74
96
  have been merged (e.g. if '2.0-release' is a branch and has no commits not in
75
97
  main, it will be deleted).
76
98
 
77
- There is even `sj gbclean` ("global branch clean") (and `sj gbcleanall`) which will
78
- do both the local and remote cleaning.
99
+ There is even `sj gbclean` ("global branch clean") (and `sj gbcleanall`) which
100
+ will do both the local and remote cleaning.
79
101
 
80
102
  *NOTE*: Remote branch cleaning is still experimental, use with caution!
81
103
 
@@ -85,7 +107,9 @@ There's a pattern to every new repo we want to contribute to. First we fork,
85
107
  then we clone the fork, then we add a remote of the upstream repo. It's
86
108
  monotonous. SugarJar does this for you:
87
109
 
88
- ![smartclone screenshot](https://github.com/jaymzh/sugarjar/blob/main/images/sclone.png)
110
+ ![smartclone screenshot](
111
+ https://github.com/jaymzh/sugarjar/blob/main/images/sclone.png
112
+ )
89
113
 
90
114
  `sj` accepts both `smartclone` and `sclone` for this command.
91
115
 
@@ -112,7 +136,9 @@ First, and foremost, is `feature` and `subfeature`. Regardless of stacking, the
112
136
  way to create a new feature bracnh with sugarjar is with `sj feature` (or `sj
113
137
  f` for short):
114
138
 
115
- ![feature screenshot](https://github.com/jaymzh/sugarjar/blob/main/images/feature.png)
139
+ ![feature screenshot](
140
+ https://github.com/jaymzh/sugarjar/blob/main/images/feature.png
141
+ )
116
142
 
117
143
  A "feature" in SugarJar parlance just means that the branch is always created
118
144
  from "most main" - this is usually `upstream/main`, but SJ will figure out
@@ -123,7 +149,9 @@ to fetch that remote first to make sure you're working on the latest HEAD.
123
149
  When you want to create a stacked PR, you can create `subfeature`, which, at
124
150
  its core is just a branch created from the current branch:
125
151
 
126
- ![subfeature screenshot](https://github.com/jaymzh/sugarjar/blob/main/images/subfeature.png)
152
+ ![subfeature screenshot](
153
+ https://github.com/jaymzh/sugarjar/blob/main/images/subfeature.png
154
+ )
127
155
 
128
156
  If you create branches like this then sugarjar can now make several things
129
157
  much easier:
@@ -134,31 +162,41 @@ much easier:
134
162
 
135
163
  There are two commands that will show you the state of your stacked branches:
136
164
 
137
- * `sj binfo` - shows the current branch and its ancestors up to your primary branch
165
+ * `sj binfo` - shows the current branch and its ancestors up to your primary
166
+ branch
138
167
  * `sj smartlog` (aka `sj sl`) - shows you the whole tree.
139
168
 
140
169
  To continue with the example above, my `smartlog` might look like:
141
170
 
142
- ![subfeature-smartlog screenshot](https://github.com/jaymzh/sugarjar/blob/main/images/subfeature-smartlog.png)
171
+ ![subfeature-smartlog screenshot](
172
+ https://github.com/jaymzh/sugarjar/blob/main/images/subfeature-smartlog.png
173
+ )
143
174
 
144
175
  As you can see, `mynewthing` is derived from `main`, and `dependentnewthing` is
145
176
  derived from `mynewthing`.
146
177
 
147
178
  Now lets make a different feature stack:
148
179
 
149
- ![subfeature-part2 screenshot](https://github.com/jaymzh/sugarjar/blob/main/images/subfeature-part2.png)
180
+ ![subfeature-part2 screenshot](
181
+ https://github.com/jaymzh/sugarjar/blob/main/images/subfeature-part2.png
182
+ )
150
183
 
151
184
  The `smartlog` will now show us this tree, and it's a bit more interesting:
152
185
 
153
- ![subfeature-part2-smartlog screenshot](https://github.com/jaymzh/sugarjar/blob/main/images/subfeature-part2-smartlog.png)
186
+ ![subfeature-part2-smartlog screenshot](
187
+ https://github.com/jaymzh/sugarjar/blob/main/images/subfeature-part2-smartlog.png
188
+ )
154
189
 
155
190
  Here we can see from `main`, we have two branches: one going to `mynewthing`
156
191
  and one going to `anotherfeature`. Each of those has their own dependent branch
157
192
  on top.
158
193
 
159
- Now, what happens if I make a change to `mynewthing` (the bottom of the first stack)?
194
+ Now, what happens if I make a change to `mynewthing` (the bottom of the first
195
+ stack)?
160
196
 
161
- ![subfeature-part3 screenshot](https://github.com/jaymzh/sugarjar/blob/main/images/subfeature-part3.png)
197
+ ![subfeature-part3 screenshot](
198
+ https://github.com/jaymzh/sugarjar/blob/main/images/subfeature-part3.png
199
+ )
162
200
 
163
201
  We can see here now that `dependentnewthing`, is based off a commit that _used_
164
202
  to be `mynewthing` (`5086ee`), but `mynewthing` has moved. Both `mynewthing`
@@ -166,7 +204,9 @@ and `dependentnewthing` are derived from `5086ee` (the old `mynewthing`), but
166
204
  `dependentnewthing` isn't (yet) based on the current `mynewthing`. But SugarJar
167
205
  will handle this all correctly when we ask it to update the branch:
168
206
 
169
- ![subfeature-part3-rebase screenshot](https://github.com/jaymzh/sugarjar/blob/main/images/subfeature-part3-rebase.png)
207
+ ![subfeature-part3-rebase screenshot](
208
+ https://github.com/jaymzh/sugarjar/blob/main/images/subfeature-part3-rebase.png
209
+ )
170
210
 
171
211
  Here we see that SugarJar knew that `dependentnewthing` should be rebased onto
172
212
  `mynewthing`, and it did the right thing - from main there's still the
@@ -177,7 +217,9 @@ including all 3 commits in the right order.
177
217
  Now, lets say that `mynewthing` gets merged and we use `bclean` to clean it all
178
218
  up, what happens then?
179
219
 
180
- ![subfeature-detect-missing-base screenshot](https://github.com/jaymzh/sugarjar/blob/main/images/subfeature-detect-missing-base.png)
220
+ ![subfeature-detect-missing-base screenshot](
221
+ https://github.com/jaymzh/sugarjar/blob/main/images/subfeature-detect-missing-base.png
222
+ )
181
223
 
182
224
  SugarJar detects that branch is gone and thus this branch should now be based
183
225
  on the upstream main branch!
@@ -196,6 +238,24 @@ It looks like this is a subfeature, would you like to base this PR on mynewthing
196
238
  ...
197
239
  ```
198
240
 
241
+ ### Smart release branch handling
242
+
243
+ You can tell sugar what release branches exist, and it will intelligently
244
+ handle them. So of you specify, in your repoconfig:
245
+
246
+ ```yaml
247
+ release_branches: ['v1-branch', 'v2-branch']
248
+ ```
249
+
250
+ Then:
251
+
252
+ * `sj feature v1-backport-foo v2-branch` will automatically base this
253
+ branch on `upstream/v2-branch` (or `origin/v2-branch` as appropriate)
254
+ * `sj feature v2-branch` will checkout `v2-branch` with the upstream set
255
+ to `upstream/v2-branch` (or `origin/v2-branch` as appropriate)
256
+ * `sj lbclean`/`sj lbcleanall` (of all varieties) will never reap release
257
+ branches
258
+
199
259
  ### Have a better lint/unittest experience!
200
260
 
201
261
  Ever made a PR, only to find out later that it failed tests because of some
@@ -276,8 +336,9 @@ Created feature branch dependent-feature based on test-branch
276
336
 
277
337
  Additionally you can specify a `feature_prefix` in your config which will cause
278
338
  `feature` to create branches prefixed with your `feature_prefix` and will also
279
- cause `co` to checkout branches with that prefix. This is useful when organizations
280
- use branch-based workflows and branches need to be prefixed with e.g. `$USER/`.
339
+ cause `co` to checkout branches with that prefix. This is useful when
340
+ organizations use branch-based workflows and branches need to be prefixed with
341
+ e.g. `$USER/`.
281
342
 
282
343
  For example, if your prefix was `user/`, then `sj feature foo` would create
283
344
  `user/foo`, and `sj co foo` would switch to `user/foo`.
@@ -287,7 +348,9 @@ For example, if your prefix was `user/`, then `sj feature foo` would create
287
348
  Smartlog will show you a tree diagram of your branches! Simply run `sj
288
349
  smartlog` or `sj sl` for short.
289
350
 
290
- ![smartlog screenshot](https://github.com/jaymzh/sugarjar/blob/main/images/smartlog.png)
351
+ ![smartlog screenshot](
352
+ https://github.com/jaymzh/sugarjar/blob/main/images/smartlog.png
353
+ )
291
354
 
292
355
  ### Sync work across workstations
293
356
 
@@ -306,7 +369,7 @@ branch, it rebases on top of the push target branch.
306
369
 
307
370
  ### Pulling in suggestions from the web
308
371
 
309
- When someone 'suggests' a change in the GitHub WebUI, once you choose to commit
372
+ When someone 'suggests' a change in the GH/GL WebUI, once you choose to commit
310
373
  them, your origin and local branches are no longer in-sync. The
311
374
  `pullsuggestions` command will attempt to merge in any remote commits to your
312
375
  local branch. This command will show a diff and ask for confirmation before
@@ -322,19 +385,22 @@ See `sj help` for more commands!
322
385
  Sugarjar is packaged in a variety of Linux distributions - see if it's on the
323
386
  list here, and if so, use your package manager (or `gem`) to install it:
324
387
 
325
- [![Packaging status](https://repology.org/badge/vertical-allrepos/sugarjar.svg?exclude_unsupported=1)](https://repology.org/project/sugarjar/versions)
388
+ [![Packaging status](
389
+ https://repology.org/badge/vertical-allrepos/sugarjar.svg?exclude_unsupported=1
390
+ )](https://repology.org/project/sugarjar/versions)
326
391
 
327
392
  If you are using a Linux distribution version that is end-of-life'd, click the
328
393
  above image, it'll take you to a page that lists unsupported distro versions
329
394
  as well (they'll have older SugarJar, but they'll probably still have some
330
395
  version).
331
396
 
332
- Ubuntu users, Ubuntu versions prior to 24.x cannot be updated, so if you're on
333
- an older Ubuntu please use [this
334
- PPA](https://launchpad.net/~michel-slm/+archive/ubuntu/sugarjar) from our
397
+ **Ubuntu users**: You can use [this PPA](
398
+ https://launchpad.net/~michel-slm/+archive/ubuntu/sugarjar) to get newer
399
+ versions for all supported Ubuntu releases (as well as some older versions).
335
400
  Ubuntu package maintainer.
336
401
 
337
- For MacOS users, we recommend using Homebrew - SugarJar is now in Homebrew Core.
402
+ **MacOS users**: We recommend using Homebrew - we keep SugarJar updated in
403
+ Homebrew Core.
338
404
 
339
405
  Finally, if none of those work for you, you can clone this repo and run it
340
406
  directly from there.
@@ -351,8 +417,8 @@ See [examples/sample_config.yaml](examples/sample_config.yaml) for an example
351
417
  configuration file.
352
418
 
353
419
  In addition, the environment variable `SUGARJAR_LOGLEVEL` can be defined to set
354
- a log level. This is primarily used as a way to turn debug on earlier in order to
355
- troubleshoot configuration parsing.
420
+ a log level. This is primarily used as a way to turn debug on earlier in order
421
+ to troubleshoot configuration parsing.
356
422
 
357
423
  Deprecated fields will cause a warning, but you can suppress that warning by
358
424
  defining `ignore_deprecated_options`, for example:
@@ -379,30 +445,31 @@ commit template by dropping a file in the repo. Users must do something like:
379
445
  `git config commit.template <file>`. Making each developer do this is error
380
446
  prone, so this setting will automatically set this up for each developer.
381
447
 
382
- ## Enterprise GitHub
448
+ ## Enterprise GitHub/GitLab
383
449
 
384
- Like `gh`, SugarJar supports GitHub Enterprise. In fact, we provide extra
385
- features just for it.
450
+ Like `gh` and `glab`, SugarJar supports Enterprise versions of GitHub and
451
+ GitLab. In fact, we provide extra features just for it.
386
452
 
387
- You can set `github_host` in your global or user config, but since most
453
+ In most cases, when using `sj smartclone`, pass in `--forge-host`, and
454
+ that's about all you need, everything else should be handlded automagically.
455
+
456
+ However, you can set `forge_host` in your global or user config, but since most
388
457
  users will also have a few opensource repos, you can override it in the
389
458
  Repository Config as well.
390
459
 
391
460
  So, for example you might have:
392
461
 
393
462
  ```yaml
394
- github_host: gh.sample.com
463
+ forge_host: gh.sample.com
395
464
  ```
396
465
 
397
466
  In your `~/.config/sugarjar/config.yaml`, but if the `.sugarjar.yaml` in your
398
467
  repo has:
399
468
 
400
469
  ```yaml
401
- github_host: github.com
470
+ forge_host: github.com
402
471
  ```
403
472
 
404
- Then we will configure `gh` to talk to github.com when in that repo.
405
-
406
473
  ## FAQ
407
474
 
408
475
  **Why the name SugarJar?**
@@ -411,7 +478,7 @@ It's mostly a backronym. Like jellyfish, I wanted two letters that were on home
411
478
  row on different sides of the keyboard to make it easy to type. I looked at the
412
479
  possible options that where there and not taken and tried to find one I could
413
480
  make an appropriate name out of. Since this utility adds lots of sugar to git
414
- and github, it seemed appropriate.
481
+ and github/gitlab, it seemed appropriate.
415
482
 
416
483
  **I'd like to package SugarJar for my favorite distro/OS, is that OK?**
417
484
 
@@ -432,12 +499,12 @@ simply source that in your dotfiles, assuming you are using bash.
432
499
 
433
500
  **What happens now that Sapling is released?**
434
501
 
435
- SugarJar isn't going anywhere anytime soon. This was meant to replace arc/jf,
436
- which has now been open-sourced as [Sapling](https://sapling-scm.com/), so I
437
- highly recommend taking a look at that!
502
+ SugarJar isn't going anywhere. This was meant to replace arc/jf, which has now
503
+ been open-sourced as [Sapling](https://sapling-scm.com/), so I highly recommend
504
+ taking a look at that!
438
505
 
439
506
  Sapling is a great tool and solves a variety of problems SugarJar will never be
440
- able to. However, it is a significant workflow change, that won't be
441
- appropriate for all users or use-cases. Similarly there are workflows and tools
442
- that Sapling breaks. So worry not, SugarJar will continue to be maintained and
443
- developed.
507
+ able to. However, it is a significant workflow change that won't be appropriate
508
+ for all users or use-cases. Similarly there are workflows and tools that
509
+ Sapling breaks. Further, we support some things Sapling does not. So worry not,
510
+ SugarJar will continue to be maintained and developed.
data/bin/sj CHANGED
@@ -36,20 +36,36 @@ parser = OptionParser.new do |opts|
36
36
  end
37
37
 
38
38
  opts.on(
39
+ '--forge-host HOST',
39
40
  '--github-host HOST',
40
- 'The host for "hub". Note that we will set this in the local repo ' +
41
- 'config so there is no need to have multiple config files for multiple ' +
42
- 'github servers. Put your default one in your config file, and simply ' +
43
- 'specify this option the first time you clone or touch a repo and it ' +
44
- 'will be part of that repo until changed.',
41
+ 'The host of your forge (github, gitlab, etc.). Generally only needed' +
42
+ ' when cloning (smartclone), as we can usually figure out from within' +
43
+ ' a cloned repo. Currently accepts --github-host for backwards' +
44
+ ' compatibility.',
45
45
  ) do |host|
46
- options['github_host'] = host
46
+ options['forge_host'] = host
47
47
  end
48
48
 
49
- opts.on('--github-user USER', 'Github username') do |user|
49
+ opts.on('--forge-type TYPE', 'Forge type: github, gitlab') do |type|
50
+ options['forge_type'] = type
51
+ end
52
+
53
+ opts.on(
54
+ '--github-user USER',
55
+ 'User for github repos, unless specified in the repoconfig.' +
56
+ ' Defaults to your local username',
57
+ ) do |user|
50
58
  options['github_user'] = user
51
59
  end
52
60
 
61
+ opts.on(
62
+ '--gitlab-user USER',
63
+ 'User for gitlab repos, unless specified in the repoconfig.' +
64
+ ' Defaults to your local username',
65
+ ) do |user|
66
+ options['gitlab_user'] = user
67
+ end
68
+
53
69
  opts.on('-h', '--help', 'Print this help message') do
54
70
  puts opts
55
71
  exit
@@ -148,6 +164,10 @@ COMMANDS:
148
164
  branches. Very convenient for keeping the branch behind a pull-
149
165
  request clean.
150
166
 
167
+ forcesync, fsync
168
+ See 'sync' below, but never tries to rebase, always does a
169
+ hard reset.
170
+
151
171
  globalbranchclean, gbclean [<branch>] [<remote>]
152
172
  WARNING: EXPERIMENTAL COMMAND.
153
173
 
@@ -306,6 +326,12 @@ end
306
326
 
307
327
  SugarJar::Log.debug("Final config: #{options}")
308
328
 
329
+ # if the command is help, we don't bother to create the Commands obj
330
+ if subcommand == 'help'
331
+ puts parser
332
+ exit
333
+ end
334
+
309
335
  sj = SugarJar::Commands.new(options)
310
336
  valid_commands = sj.public_methods - Object.public_methods
311
337
  is_valid_command = valid_commands.include?(subcommand.to_sym)
@@ -321,13 +347,7 @@ SugarJar::Log.debug("subcommand is #{subcommand}")
321
347
  extra_opts += argv_copy
322
348
  SugarJar::Log.debug("extra unknown options: #{extra_opts}")
323
349
 
324
- case subcommand
325
- when 'help'
326
- puts parser
327
- exit
328
- when 'debuginfo'
329
- extra_opts = [options]
330
- end
350
+ extra_opts = [options] if subcommand == 'debuginfo'
331
351
 
332
352
  unless is_valid_command
333
353
  SugarJar::Log.fatal("No such subcommand: #{subcommand}")
@@ -22,3 +22,9 @@ pr_autostack: true
22
22
  # Don't warn about deprecated config file options if they are in this
23
23
  # list
24
24
  ignore_deprecated_options: [ 'gh_cli' ]
25
+
26
+ # User to use when cloning new github repos
27
+ github_user: c00ldude
28
+
29
+ # User to use when cloning new gitlab repos
30
+ gitlab_user: c00ldude
@@ -20,6 +20,16 @@ include_from: .sugarjar_local.yaml
20
20
 
21
21
  overwrite_from: .sugarjar_local_overwrite.yaml
22
22
 
23
+ # `release_branches` tells SugarJar several things:
24
+ # 1. These branches should not be repead when running `bclean`/`bcleanall`.
25
+ # 2. When a feature-branch is made from a release branch (e.g. `2.x-branch`),
26
+ # it will actually track the release branch's upstream
27
+ # (e.g. `upstream/2.x-bramch`), allowing it to work the same as as a
28
+ # feature made off of main.
29
+ release_branches:
30
+ - 2.x-branch
31
+ - 3.x-branch
32
+
23
33
  # `lint` is a list of scripts to run when `sj lint` is executed (or, if
24
34
  # configured, to run on `sj spush`/`sj fpush` - see `on_push` below).
25
35
  # Regardless of where `sj` is run from, these scripts will be run from the root
@@ -71,7 +81,13 @@ commit_template: .git_commit_template.txt
71
81
 
72
82
  github_user: myuser
73
83
 
74
- # `github_host` is the GitHub host to use when talking to GitHub (for hosted
75
- # GHE). See `github_user`.
84
+ # `gitlab_user` is the user to use when talking to GitLab. Overrides any such
85
+ # setting in the regular SugarJar config. Most useful when in the
86
+ # `include_from` file.
87
+
88
+ gitlab_user: myuser
89
+
90
+ # `forge_host` is the GitHub/GitLab host to use when talking to hosted versions
91
+ # of these services.
76
92
 
77
- github_host: github.sample.com
93
+ forge_host: github.sample.com
@@ -5,10 +5,11 @@ class SugarJar
5
5
  name ||= current_branch
6
6
  name = fprefix(name)
7
7
 
8
- wt_branches = worktree_branches
9
-
10
- if wt_branches.include?(name)
11
- SugarJar::Log.warn("#{name}: #{color('skipped', :yellow)} (worktree)")
8
+ should_skip, why = skip_branch_info(name)
9
+ if should_skip
10
+ msg = "#{name}: #{color('skipped', :yellow)}"
11
+ msg << " (#{why})" if why
12
+ SugarJar::Log.warn(msg)
12
13
  return
13
14
  end
14
15
 
@@ -61,16 +62,17 @@ class SugarJar
61
62
  def lbcleanall
62
63
  assert_in_repo!
63
64
  curr = current_branch
64
- wt_branches = worktree_branches
65
+ worktree_branches
65
66
  all_local_branches.each do |branch|
66
- if MAIN_BRANCHES.include?(branch)
67
- SugarJar::Log.debug("Skipping #{branch}")
68
- next
69
- end
70
- if wt_branches.include?(branch)
71
- SugarJar::Log.info(
72
- "#{branch}: #{color('skipped', :yellow)} (worktree)",
73
- )
67
+ # skip_branch info will check for MAIN_BRANCHES, but we
68
+ # quietly skip them.
69
+ next if MAIN_BRANCHES.include?(branch)
70
+
71
+ should_skip, why = skip_branch_info(branch)
72
+ if should_skip
73
+ msg = "#{branch}: #{color('skipped', :yellow)}"
74
+ msg << " (#{why})" if why
75
+ SugarJar::Log.info(msg)
74
76
  next
75
77
  end
76
78
 
@@ -229,5 +231,18 @@ class SugarJar
229
231
  # delete our temp branch
230
232
  git('branch', '-D', tmp)
231
233
  end
234
+
235
+ def skip_branch_info(name)
236
+ return true, 'main branch' if MAIN_BRANCHES.include?(name)
237
+
238
+ wt_branches = worktree_branches
239
+ rel_branches = release_branches
240
+
241
+ return true, 'worktree' if wt_branches.include?(name)
242
+
243
+ return true, 'release branch' if rel_branches.include?(name)
244
+
245
+ [false, nil]
246
+ end
232
247
  end
233
248
  end
@@ -22,12 +22,12 @@ class SugarJar
22
22
  exit(1)
23
23
  end
24
24
  end
25
- exit(1) unless run_check('lint')
25
+ exit(1) unless run_check('lint', false)
26
26
  end
27
27
 
28
28
  def unit
29
29
  assert_in_repo!
30
- exit(1) unless run_check('unit')
30
+ exit(1) unless run_check('unit', false)
31
31
  end
32
32
 
33
33
  def get_checks_from_command(type)
@@ -71,7 +71,13 @@ class SugarJar
71
71
  @checks[type]
72
72
  end
73
73
 
74
- def run_check(type)
74
+ # autorun is true when we're running from push, and false when someone
75
+ # ran 'lint' or 'unit' directly
76
+ #
77
+ # In the case of a autorun, if a linter changes the code, we require
78
+ # either the user amend, or bail out. If it's a manual run, then we
79
+ # allow them to just go on.
80
+ def run_check(type, autorun)
75
81
  repo_root = SugarJar::Util.repo_root
76
82
  Dir.chdir repo_root do
77
83
  checks = get_checks(type)
@@ -81,6 +87,7 @@ class SugarJar
81
87
 
82
88
  checks.each do |check|
83
89
  SugarJar::Log.debug("Running #{type} #{check}")
90
+ skip_redo = false
84
91
 
85
92
  short = check.split.first
86
93
  if short.include?('/')
@@ -104,10 +111,15 @@ class SugarJar
104
111
  )
105
112
  puts git('diff').stdout
106
113
  loop do
107
- $stdout.print(
108
- "\nWould you like to\n\t[q]uit and inspect\n\t[a]mend the " +
109
- "changes to the current commit and re-run\n > ",
110
- )
114
+ options = [
115
+ '[q]uit and inspect',
116
+ '[a]mend the changes to the current commit and re-run',
117
+ ]
118
+ options << '[i]gnore the changes and keep going' unless autorun
119
+
120
+ msg = "\nWould you like to\n\t" + options.join("\n\t") + "\n > "
121
+
122
+ $stdout.print(msg)
111
123
  ans = $stdin.gets.strip
112
124
  case ans
113
125
  when /^q/
@@ -118,9 +130,14 @@ class SugarJar
118
130
  # break here, if we get out of this loop we 'redo', assuming
119
131
  # the user chose this option
120
132
  break
133
+ when /^i/
134
+ unless autorun
135
+ skip_redo = true
136
+ break
137
+ end
121
138
  end
122
139
  end
123
- redo
140
+ redo unless skip_redo
124
141
  end
125
142
 
126
143
  if s.error?
@@ -4,7 +4,7 @@ class SugarJar
4
4
  class Commands
5
5
  def debuginfo(*args)
6
6
  puts "sugarjar version #{SugarJar::VERSION}"
7
- puts ghcli('version').stdout
7
+ puts forge('version').stdout
8
8
  puts git('version').stdout
9
9
 
10
10
  puts "Config: #{JSON.pretty_generate(args[0])}"
@@ -5,10 +5,32 @@ class SugarJar
5
5
  SugarJar::Log.debug("Feature: #{name}, #{base}")
6
6
  name = fprefix(name)
7
7
  die("#{name} already exists!") if all_local_branches.include?(name)
8
+ rel_branches = release_branches
8
9
  if base
9
- fbase = fprefix(base)
10
- base = fbase if all_local_branches.include?(fbase)
10
+ # If the user specified a base branch (sf mything base)
11
+ # we check if <base> is a release branch and if so, we make
12
+ # this track <upstream>/<base>
13
+ if rel_branches.include?(base)
14
+ newbase = "#{upstream}/#{base}"
15
+ SugarJar::Log.info(
16
+ "Base branch #{base} is a release branch, setting it to track " +
17
+ newbase,
18
+ )
19
+ base = newbase
20
+ else
21
+ fbase = fprefix(base)
22
+ base = fbase if all_local_branches.include?(fbase)
23
+ end
24
+ elsif rel_branches.include?(name)
25
+ # If the user did NOT specify a base *and* this new feature is
26
+ # a release branch, check it out tracking the upstream release
27
+ # branch instead of main
28
+ base = "#{upstream}/#{name}"
29
+ SugarJar::Log.info(
30
+ "Feature #{name} is a release branch, setting it to track #{base}",
31
+ )
11
32
  else
33
+ # otherwise, fallback to most-main
12
34
  base ||= most_main
13
35
  end
14
36
  # If our base is a local branch, don't try to parse it for a remote name
@@ -25,6 +47,7 @@ class SugarJar
25
47
  end
26
48
  alias f feature
27
49
 
50
+ # alias for "feature <current_branch>'
28
51
  def subfeature(name)
29
52
  assert_in_repo!
30
53
  SugarJar::Log.debug("Subfature: #{name}")
@@ -43,7 +43,7 @@ class SugarJar
43
43
  def run_prepush
44
44
  @repo_config['on_push']&.each do |item|
45
45
  SugarJar::Log.debug("Running on_push check type #{item}")
46
- unless run_check(item)
46
+ unless run_check(item, true)
47
47
  SugarJar::Log.info("[prepush]: #{item} #{color('failed', :red)}.")
48
48
  return false
49
49
  end
@@ -1,7 +1,7 @@
1
1
  class SugarJar
2
2
  class Commands
3
3
  def smartclone(repo, dir = nil, *)
4
- reponame = File.basename(repo, '.git')
4
+ reponame = extract_repo(repo)
5
5
  dir ||= reponame
6
6
  org = extract_org(repo)
7
7
 
@@ -13,10 +13,15 @@ class SugarJar
13
13
  #
14
14
  # Unless the repo is in our own org and cannot be forked, then it
15
15
  # will fail.
16
- if org == @ghuser
16
+ if org == @forge_user
17
17
  git('clone', canonicalize_repo(repo), dir, *)
18
18
  else
19
- ghcli('repo', 'fork', '--clone', canonicalize_repo(repo), dir, *)
19
+ if @repo_forge == 'gitlab'
20
+ _gitlab_clone(org, repo, dir, *)
21
+ else
22
+ forge('repo', 'fork', '--clone', canonicalize_repo(repo), dir, *)
23
+ end
24
+
20
25
  # make the main branch track upstream
21
26
  Dir.chdir dir do
22
27
  git('branch', '-u', "upstream/#{main_branch}")
@@ -26,5 +31,44 @@ class SugarJar
26
31
  SugarJar::Log.info('Remotes "origin" and "upstream" configured.')
27
32
  end
28
33
  alias sclone smartclone
34
+
35
+ def _gitlab_clone(_org, repo, dir, *)
36
+ # The gitlab CLI is much less forgiving about already-forked
37
+ # repos, and it has no option to clone to a differently-named
38
+ # directory. So we have to special case it.
39
+
40
+ # glab requires a short-name for the fork command...
41
+ shortname = repo_shortname(repo)
42
+
43
+ # We call fork without --clone since --clone can't clone
44
+ # to another directory. Also, we must specify =false, or it
45
+ # will prompt
46
+ s = forge_nofail('repo', 'fork', shortname, '--clone=false')
47
+
48
+ # It fails with:
49
+ # 409 {message: [Project namespace name has already been taken,
50
+ # Name has already been taken, Path has already been taken]}
51
+ #
52
+ # when there's already a fork... or if you happen to have a name
53
+ # collision. There's no way to tell, so we assume it means we've
54
+ # already forked.
55
+ if s.error?
56
+ if s.stderr.include?(' 409 ')
57
+ SugarJar::Log.debug('Forking failed, probably already forked')
58
+ else
59
+ s.error!
60
+ end
61
+ end
62
+
63
+ # Now we clone ourselves...
64
+ git('clone', canonicalize_repo(repo), dir, *)
65
+ Dir.chdir dir do
66
+ # and then configure remotes properly
67
+ git('remote', 'rename', 'origin', 'upstream')
68
+
69
+ fork_url = forked_repo(repo, @forge_user)
70
+ git('remote', 'add', 'origin', fork_url)
71
+ end
72
+ end
29
73
  end
30
74
  end
@@ -60,10 +60,25 @@ class SugarJar
60
60
 
61
61
  # <org>:<branch> is the GH API syntax for:
62
62
  # look for a branch of name <branch>, from a fork in owner <org>
63
- args.unshift('--head', "#{push_org}:#{curr}")
64
- SugarJar::Log.trace("Running: gh pr create #{args.join(' ')}")
65
- gh = SugarJar::Util.which('gh')
66
- system(gh, 'pr', 'create', *args)
63
+ if @repo_forge == 'github'
64
+ # On GitHub, the head is the org and the *BRANCH* name to use as
65
+ # the head branch...
66
+ args.unshift('--head', "#{push_org}:#{curr}")
67
+ else
68
+ # On GitLab, the head is the repo (org/repo) to use as the head
69
+ # _repo_, and then branch is configured seperately (with -s), but
70
+ # we don't need that since it defaults to the local branch name.
71
+ #
72
+ # Then we need --yes for it to not prompt us
73
+ args.unshift('--head', "#{push_org}/#{reponame}", '--yes')
74
+ end
75
+
76
+ bin = SugarJar::Util.which(_forge_cmd)
77
+ subcmd = _pr_cmd
78
+ SugarJar::Log.trace(
79
+ "Running: #{bin} #{subcmd} create #{args.join(' ')}",
80
+ )
81
+ system(bin, subcmd, 'create', *args)
67
82
  end
68
83
 
69
84
  alias spr smartpullrequest
@@ -71,6 +86,10 @@ class SugarJar
71
86
 
72
87
  private
73
88
 
89
+ def _pr_cmd
90
+ @repo_forge == 'gitlab' ? 'mr' : 'pr'
91
+ end
92
+
74
93
  def assert_common_main_branch!
75
94
  upstream_branch = main_remote_branch(upstream)
76
95
  unless main_branch == upstream_branch
@@ -87,7 +106,9 @@ class SugarJar
87
106
  return if upstream_branch == 'origin'
88
107
 
89
108
  origin_branch = main_remote_branch('origin')
90
- return if origin_branch == upstream_branch
109
+ # NOTE: that on GL, forks don't fork any branches by default, even
110
+ # a main one, so if it's 'nil', then ignore.
111
+ return if origin_branch.nil? || origin_branch == upstream_branch
91
112
 
92
113
  die(
93
114
  "The main branch of your upstream (#{upstream_branch}) and your " +
@@ -53,21 +53,53 @@ class SugarJar
53
53
  end
54
54
  end
55
55
 
56
- def sync
56
+ def fsync
57
+ sync(:force => true)
58
+ end
59
+ alias forcesync fsync
60
+
61
+ def sync(force: false)
57
62
  assert_in_repo!
58
63
  dirty_check!
59
64
 
60
65
  src = "origin/#{current_branch}"
61
66
  fetch('origin')
62
- s = git_nofail('merge-base', '--is-ancestor', 'HEAD', src)
63
- if s.error?
64
- SugarJar::Log.debug(
65
- "Choosing rebase sync since this isn't a direct ancestor",
66
- )
67
- rebase(src)
68
- else
69
- SugarJar::Log.debug('Choosing reset sync since this is an ancestor')
67
+ want_reset = false
68
+ if force
69
+ SugarJar::Log.debug('Forcing reset instead of rebase at user request')
70
+ want_reset = true
71
+ end
72
+
73
+ unless force
74
+ s = git_nofail('merge-base', '--is-ancestor', 'HEAD', src)
75
+ # if this IS an ancestor, we can just force reset.
76
+ #
77
+ # otherwise, we attempt a rebase to not lose anything (unless
78
+ # force is set)
79
+ if s.error?
80
+ SugarJar::Log.debug(
81
+ "Choosing rebase sync since this isn't a direct ancestor",
82
+ )
83
+ else
84
+ SugarJar::Log.debug('Choosing reset sync since this is an ancestor')
85
+ want_reset = true
86
+ end
87
+ end
88
+
89
+ if want_reset
70
90
  git('reset', '--hard', src)
91
+ else
92
+ rebase(src)
93
+ s = git_nofail('rev-parse', '--verify', 'REBASE_HEAD')
94
+ unless s.error?
95
+ SugarJar::Log.info(
96
+ 'Rebase required input. You may continue the rebase from' +
97
+ ' here normally, or you may abort (`git rebase --abort`)' +
98
+ ' and instead to `sj fsync` to skip a rebase and force' +
99
+ ' reset to the remote branch.',
100
+ )
101
+ return
102
+ end
71
103
  end
72
104
  SugarJar::Log.info("Synced to #{src}.")
73
105
  end
@@ -85,7 +117,7 @@ class SugarJar
85
117
  # rubocop:enable Style/HashSyntax
86
118
  unless base
87
119
  SugarJar::Log.info(
88
- 'The brach we were tracking is gone, resetting tracking to ' +
120
+ 'The branch we were tracking is gone, resetting tracking to ' +
89
121
  most_main,
90
122
  )
91
123
  git('branch', '-u', most_main)
@@ -36,13 +36,27 @@ class SugarJar
36
36
  @checks = {}
37
37
  @main_branch = nil
38
38
  @main_remote_branches = {}
39
- @ghuser = @repo_config['github_user'] || options['github_user']
40
- @ghhost = @repo_config['github_host'] || options['github_host']
39
+ # This is CONFIGURED host, which may be null, as opposed
40
+ # to the method forge_host which will always return something
41
+ @_forge_host = @repo_config['forge_host'] || options['forge_host']
42
+ @repo_forge = @repo_config['forge_type'] || options['forge_type'] ||
43
+ _determine_forge_type
44
+
45
+ unless @repo_forge.nil?
46
+ cmd = _forge_cmd
47
+ unless SugarJar::Util.which_nofail(cmd)
48
+ die("No '#{cmd}' found, please install it'")
49
+ end
50
+ end
41
51
 
42
- die("No 'gh' found, please install 'gh'") unless gh_avail?
52
+ user_option = "#{@repo_forge}_user"
53
+ @forge_user = @repo_config[user_option] || options[user_option]
43
54
 
44
- # Tell the 'gh' cli where to talk to, if not github.com
45
- ENV['GH_HOST'] = @ghhost if @ghhost
55
+ # Tell the cli where to talk to, if not default
56
+ if @_forge_host
57
+ ENV['GH_HOST'] = @_forge_host
58
+ ENV['GL_HOST'] = @_forge_host
59
+ end
46
60
 
47
61
  return if options['no_change']
48
62
 
@@ -52,12 +66,8 @@ class SugarJar
52
66
  private
53
67
 
54
68
  def forked_repo(repo, username)
55
- repo = if repo.start_with?('http', 'git@')
56
- File.basename(repo)
57
- else
58
- "#{File.basename(repo)}.git"
59
- end
60
- "git@#{@ghhost || 'github.com'}:#{username}/#{repo}"
69
+ repo = extract_repo(repo)
70
+ "git@#{forge_host}:#{username}/#{repo}.git"
61
71
  end
62
72
 
63
73
  # gh utils will default to https, but we should always default to SSH
@@ -66,12 +76,41 @@ class SugarJar
66
76
  # if they fully-qualified it, we're good
67
77
  return repo if repo.start_with?('http', 'git@')
68
78
 
69
- # otherwise, ti's a shortname
70
- cr = "git@#{@ghhost || 'github.com'}:#{repo}.git"
79
+ # otherwise, it's a shortname
80
+ cr = "git@#{forge_host}:#{repo}.git"
71
81
  SugarJar::Log.debug("canonicalized #{repo} to #{cr}")
72
82
  cr
73
83
  end
74
84
 
85
+ def forge_host
86
+ # if one is specifically configured, use that
87
+ return @_forge_host if @_forge_host
88
+
89
+ # otherwise, if we're in a repo, use the hostname of the remote
90
+ if SugarJar::Util.in_repo?
91
+ extract_host(remote_url_map['origin'])
92
+ else
93
+ @repo_forge == 'gitlab' ? 'gitlab.com' : 'github.com'
94
+ end
95
+ end
96
+
97
+ def repo_shortname(repo)
98
+ # if it's already a shortname, return
99
+ return repo unless repo.start_with?('http', 'git@')
100
+
101
+ # otherwise, parse it
102
+ if repo.start_with?('http')
103
+ bits = repo.split('/')
104
+ elsif repo.start_with?('git@')
105
+ relevant = repo.split(':').last
106
+ bits = relevant.split('/')
107
+ end
108
+ repo = bits[-1].gsub('.git', '')
109
+ org = bits[-2]
110
+
111
+ "#{org}/#{repo}"
112
+ end
113
+
75
114
  def set_commit_template
76
115
  unless SugarJar::Util.in_repo?
77
116
  SugarJar::Log.debug('Skipping set_commit_template: not in repo')
@@ -137,7 +176,11 @@ class SugarJar
137
176
  end
138
177
 
139
178
  def determine_main_branch(branches)
140
- branches.include?('main') ? 'main' : 'master'
179
+ if branches.include?('main')
180
+ 'main'
181
+ elsif branches.include?('master')
182
+ 'master'
183
+ end
141
184
  end
142
185
 
143
186
  def main_branch
@@ -263,7 +306,7 @@ class SugarJar
263
306
 
264
307
  # Whatever org we push to, regardless of if this is a fork or not
265
308
  def push_org
266
- url = git('remote', 'get-url', 'origin').stdout.strip
309
+ url = remote_url_map['origin']
267
310
  extract_org(url)
268
311
  end
269
312
 
@@ -282,8 +325,8 @@ class SugarJar
282
325
  end
283
326
  end
284
327
 
285
- def gh_avail?
286
- !!SugarJar::Util.which_nofail('gh')
328
+ def forge_cli_avail?
329
+ !!SugarJar::Util.which_nofail(_forge_cmd)
287
330
  end
288
331
 
289
332
  def fprefix(name)
@@ -323,11 +366,23 @@ class SugarJar
323
366
  File.basename(repo, '.git')
324
367
  end
325
368
 
369
+ def extract_host(repo)
370
+ if repo.start_with?('git@')
371
+ repo.split(':').first.split('@').last
372
+ elsif repo.start_with?('http')
373
+ repo.split('/')[2]
374
+ end
375
+ end
376
+
326
377
  def die(msg)
327
378
  SugarJar::Log.fatal(msg)
328
379
  exit(1)
329
380
  end
330
381
 
382
+ def release_branches
383
+ @repo_config['release_branches'] || []
384
+ end
385
+
331
386
  def worktree_branches
332
387
  worktrees.values.map do |wt|
333
388
  branch_from_ref(wt['branch'])
@@ -373,12 +428,36 @@ class SugarJar
373
428
  SugarJar::Util.git_nofail(*, :color => @color)
374
429
  end
375
430
 
376
- def ghcli(*)
377
- SugarJar::Util.ghcli(*)
431
+ def _determine_forge_type
432
+ return nil unless SugarJar::Util.in_repo?
433
+
434
+ if remote_url_map.values.any? do |x|
435
+ x.include?('gitlab')
436
+ end
437
+ 'gitlab'
438
+ else
439
+ 'github'
440
+ end
441
+ end
442
+
443
+ def _forge_cmd
444
+ @repo_forge == 'gitlab' ? 'glab' : 'gh'
445
+ end
446
+
447
+ def forge(*)
448
+ if @repo_forge == 'gitlab'
449
+ SugarJar::Util.glcli(*)
450
+ else
451
+ SugarJar::Util.ghcli(*)
452
+ end
378
453
  end
379
454
 
380
- def ghcli_nofail(*)
381
- SugarJar::Util.ghcli_nofail(*)
455
+ def forge_nofail(*)
456
+ if @repo_forge == 'gitlab'
457
+ SugarJar::Util.glcli_nofail(*)
458
+ else
459
+ SugarJar::Util.ghcli_nofail(*)
460
+ end
382
461
  end
383
462
  end
384
463
  end
@@ -7,6 +7,7 @@ class SugarJar
7
7
  class Config
8
8
  DEFAULTS = {
9
9
  'github_user' => ENV.fetch('USER'),
10
+ 'gitlab_user' => ENV.fetch('USER'),
10
11
  'pr_autofill' => true,
11
12
  'pr_autostack' => nil,
12
13
  'color' => true,
@@ -27,6 +28,10 @@ class SugarJar
27
28
  SugarJar::Log.debug("Loading config #{f}")
28
29
  data = YAML.safe_load_file(f)
29
30
  warn_on_deprecated_configs(data, f)
31
+ if data['github_host']
32
+ data['forge_host'] = data['github_host'] if data['forge_host'].nil?
33
+ data.delete('github_host')
34
+ end
30
35
  # an empty file is a `nil` which you can't merge
31
36
  c.merge!(YAML.safe_load_file(f)) if data
32
37
  SugarJar::Log.debug("Modified config: #{c}")
@@ -41,14 +46,37 @@ class SugarJar
41
46
 
42
47
  if ignore_deprecated_options.include?(opt)
43
48
  SugarJar::Log.debug(
44
- "Not warning about deprecated option '#{opt}' in #{fname} due to " +
45
- '"ignore_deprecated_options" in that file.',
49
+ "#{fname}: Not warning about deprecated option `#{opt}` due to " +
50
+ '`ignore_deprecated_options` in that file.',
46
51
  )
47
52
  next
48
53
  end
49
54
  SugarJar::Log.warn(
50
- "Config file #{fname} contains deprecated option #{opt}. You can " +
51
- 'suppress this warning with ignore_deprecated_options.',
55
+ "#{fname}: contains deprecated option `#{opt}`. You can " +
56
+ 'suppress this warning with `ignore_deprecated_options`.',
57
+ )
58
+ end
59
+
60
+ # github_host has special handling
61
+ return unless data['github_host']
62
+
63
+ if ignore_deprecated_options.include?('github_host')
64
+ SugarJar::Log.debug(
65
+ "#{fname}: Deprecated option `github_host` found, but not " +
66
+ 'warning due to `ignore_deprecated_options` in that file.',
67
+ )
68
+ elsif data.key?('forge_host')
69
+ SugarJar::Log.warn(
70
+ "#{fname}: Deprecated option `github_host` found. " +
71
+ 'Ignoring in favor of newer `force_host` option. You can ' +
72
+ 'suppress this warning with `ignore_deprecated_options`.',
73
+ )
74
+ else
75
+ SugarJar::Log.warn(
76
+ "#{fname}: Deprecated option `github_host` found. " +
77
+ 'Treating it as if it was `forge_host` for now. Please update ' +
78
+ 'your config file to use this new option. You can suppress ' +
79
+ 'this warning with `ignore_deprecated_options`.',
52
80
  )
53
81
  end
54
82
  end
data/lib/sugarjar/log.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'mixlib/log'
2
2
 
3
+ # rubocop:disable Style/OneClassPerFile
3
4
  module Mixlib
4
5
  module Log
5
6
  # A simple formatter so that 'info' is just like 'puts'
@@ -22,3 +23,4 @@ class SugarJar
22
23
  extend Mixlib::Log
23
24
  end
24
25
  end
26
+ # rubocop:enable Style/OneClassPerFile
data/lib/sugarjar/util.rb CHANGED
@@ -46,16 +46,36 @@ class SugarJar
46
46
  s
47
47
  end
48
48
 
49
- def self.ghcli_nofail(*args)
50
- SugarJar::Log.trace("Running: gh #{args.join(' ')}")
51
- gh = which('gh')
52
- s = Mixlib::ShellOut.new([gh] + args).run_command
53
- if s.error? && s.stderr.include?('gh auth')
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")
54
74
  SugarJar::Log.info(
55
- 'gh was run but no github token exists. Will run "gh auth login" ' +
56
- "to force\ngh to authenticate...",
75
+ 'glab was run but no gitlab token exists. Will run ' +
76
+ '"glab auth login" to force\ngh to authenticate...',
57
77
  )
58
- unless system(gh, 'auth', 'login', '-p', 'ssh')
78
+ unless system(bin, 'auth', 'login', '-p', 'ssh')
59
79
  SugarJar::Log.fatal(
60
80
  'That failed, I will bail out. Hub needs to get a github ' +
61
81
  'token. Try running "gh auth login" (will list info about ' +
@@ -67,12 +87,6 @@ class SugarJar
67
87
  s
68
88
  end
69
89
 
70
- def self.ghcli(*)
71
- s = ghcli_nofail(*)
72
- s.error!
73
- s
74
- end
75
-
76
90
  def self.in_repo?
77
91
  s = git_nofail('rev-parse', '--is-inside-work-tree')
78
92
  !s.error? && s.stdout.strip == 'true'
@@ -1,3 +1,3 @@
1
1
  class SugarJar
2
- VERSION = '2.0.2'.freeze
2
+ VERSION = '3.0.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sugarjar
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 3.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-01-08 00:00:00.000000000 Z
11
+ date: 2026-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deep_merge