pax_global_header 0000666 0000000 0000000 00000000064 15236151503 0014513 g ustar 00root root 0000000 0000000 52 comment=a383ed7feec3c157b6272e2e3a95fb9ae620b16c
sugarjar-4.0.0/ 0000775 0000000 0000000 00000000000 15236151503 0013332 5 ustar 00root root 0000000 0000000 sugarjar-4.0.0/.github/ 0000775 0000000 0000000 00000000000 15236151503 0014672 5 ustar 00root root 0000000 0000000 sugarjar-4.0.0/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 15236151503 0017055 5 ustar 00root root 0000000 0000000 sugarjar-4.0.0/.github/ISSUE_TEMPLATE/bug_report.md 0000664 0000000 0000000 00000000700 15236151503 0021544 0 ustar 00root root 0000000 0000000 ---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: jaymzh
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior including commands and output
**Expected behavior**
A clear and concise description of what you expected to happen.
**Environment (please complete the following information):**
- OS:
- Output of `sj version`
sugarjar-4.0.0/.github/ISSUE_TEMPLATE/feature_request.md 0000664 0000000 0000000 00000001274 15236151503 0022606 0 ustar 00root root 0000000 0000000 ---
name: Feature request
about: Suggest an idea for this project
title: "[RFE]"
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always
frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've
considered.
**Contribution**
Are you willing to write this feature? If so, would you need assistance?
**Additional context**
Add any other context or screenshots about the feature request here.
sugarjar-4.0.0/.github/ISSUE_TEMPLATE/support-request.md 0000664 0000000 0000000 00000000541 15236151503 0022601 0 ustar 00root root 0000000 0000000 ---
name: Support request
about: Use this to ask for help
title: "[support]"
labels: ''
assignees: ''
---
**Describe the problem**
Please describe the problem you are having in as much detail as possible.
**What you've tried**
Please describe what steps you've taken to try to solve the problem
**Version**
Please provide the output of `sj version`
sugarjar-4.0.0/.github/dependabot.yml 0000664 0000000 0000000 00000000617 15236151503 0017526 0 ustar 00root root 0000000 0000000 version: 2
updates:
- package-ecosystem: bundler
directory: /
schedule:
interval: monthly
groups:
all-minor-patch:
patterns: ["*"]
update-types: ["minor", "patch"]
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
groups:
all-minor-patch:
patterns: ["*"]
update-types: ["minor", "patch"]
sugarjar-4.0.0/.github/workflows/ 0000775 0000000 0000000 00000000000 15236151503 0016727 5 ustar 00root root 0000000 0000000 sugarjar-4.0.0/.github/workflows/check-all-checks.yml 0000664 0000000 0000000 00000000714 15236151503 0022535 0 ustar 00root root 0000000 0000000 name: All checks pass
on:
pull_request:
permissions: {}
jobs:
allchecks:
runs-on: ubuntu-latest
permissions:
checks: read
contents: read
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- uses: wechuli/allcheckspassed@204ff63e89eabdc8086f0c50b91f675bcf37c8f6 # v2.4.0
sugarjar-4.0.0/.github/workflows/codeql.yml 0000664 0000000 0000000 00000002113 15236151503 0020716 0 ustar 00root root 0000000 0000000 name: "CodeQL"
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
schedule:
- cron: "0 0 * * 1"
permissions:
contents: read
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ["ruby"]
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Initialize CodeQL
uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
with:
languages: ${{ matrix.language }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
with:
category: "/language:${{matrix.language}}"
sugarjar-4.0.0/.github/workflows/dco.yml 0000664 0000000 0000000 00000001361 15236151503 0020220 0 ustar 00root root 0000000 0000000 name: DCO Check
on: [pull_request]
permissions: {}
jobs:
dco_check_job:
permissions:
contents: read
pull-requests: read
runs-on: ubuntu-latest
name: DCO Check
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Get PR Commits
uses: actionshub/get-pr-commits@6fc8a0242c2c5853e3e8b86bde9d176682e5c5fb # main
id: 'get-pr-commits'
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: DCO Check
uses: actionshub/dco@736d5885f972dcfd68b3db44da9a940b370cc625 # main
with:
commits: ${{ steps.get-pr-commits.outputs.commits }}
sugarjar-4.0.0/.github/workflows/dependency-review.yml 0000664 0000000 0000000 00000001114 15236151503 0023064 0 ustar 00root root 0000000 0000000 name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: 'Checkout Repository'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: 'Dependency Review'
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
sugarjar-4.0.0/.github/workflows/lint.yml 0000664 0000000 0000000 00000004143 15236151503 0020422 0 ustar 00root root 0000000 0000000 name: Lint
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
permissions: {}
jobs:
# Rather than setup ruby several times, we run rubocop and mdl in
# the same place, until the actionshub mdl checker is working again
ruby-linters:
permissions:
contents: read
pull-requests: read
strategy:
fail-fast: false
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup ruby
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
with:
ruby-version: '3.2'
- name: install deps
run: bundle install
- name: Run rubocop
run: ./scripts/run_rubocop.sh
- name: Run mdl
run: ./scripts/run_mdl.sh
#markdownlint:
# permissions:
# contents: read
# pull-requests: read
# runs-on: ubuntu-latest
# steps:
# - name: Harden the runner (Audit all outbound calls)
# uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
# with:
# egress-policy: audit
# - name: checkout
# uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# - name: MarkdownLint mdl Action
# uses: actionshub/markdownlint@6c82ff529253530dfbf75c37570876c52692835f # 3.1.4
linelint:
permissions:
contents: read
pull-requests: read
runs-on: ubuntu-latest
name: Check if all files end in newline
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Linelint
uses: fernandrone/linelint@7907a5dca0c28ea7dd05c6d8d8cacded713aca11 # master
id: linelint
sugarjar-4.0.0/.github/workflows/unit.yml 0000664 0000000 0000000 00000001746 15236151503 0020441 0 ustar 00root root 0000000 0000000 name: Unittests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
permissions: {}
jobs:
rspec:
permissions:
contents: read
pull-requests: read
strategy:
fail-fast: false
matrix:
ruby: [3.2, 3.3, 3.4]
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Install system dependencies
run: |-
sudo apt update
sudo apt install -y gh glab
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Ruby
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
with:
ruby-version: ${{ matrix.ruby }}
- name: Install Ruby dependencies
run: bundle install
- name: Run rspec
run: ./scripts/run_rspec.sh
sugarjar-4.0.0/.gitignore 0000664 0000000 0000000 00000002336 15236151503 0015326 0 ustar 00root root 0000000 0000000 *.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/
# Used by dotenv library to load environment variables.
# .env
# Ignore Byebug command history file.
.byebug_history
## Specific to RubyMotion:
.dat*
.repl_history
build/
*.bridgesupport
build-iPhoneOS/
build-iPhoneSimulator/
## Specific to RubyMotion (use of CocoaPods):
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# vendor/Pods/
## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/
## Environment normalization:
/.bundle/
/vendor/bundle
/lib/bundler/man/
# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# Gemfile.lock
# .ruby-version
# .ruby-gemset
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
# .rubocop-https?--*
packaging/.vagrant
noarch
.ruby-version
sugarjar-4.0.0/.linelint.yml 0000664 0000000 0000000 00000000101 15236151503 0015741 0 ustar 00root root 0000000 0000000 rules:
end-of-file:
enable: true
single-new-line: true
sugarjar-4.0.0/.mdl_style.rb 0000664 0000000 0000000 00000000177 15236151503 0015736 0 ustar 00root root 0000000 0000000 all
rule 'MD013', :ignore_code_blocks => true
rule 'MD026', :punctuation => '.,:;'
exclude_rule 'MD041'
exclude_rule 'MD033'
sugarjar-4.0.0/.mdlrc 0000664 0000000 0000000 00000000054 15236151503 0014433 0 ustar 00root root 0000000 0000000 style '.mdl_style.rb'
parse_block_html true
sugarjar-4.0.0/.rubocop.yml 0000664 0000000 0000000 00000002222 15236151503 0015602 0 ustar 00root root 0000000 0000000 AllCops:
TargetRubyVersion: 3.2
NewCops: enable
Exclude:
- 'rubygem-sugarjar.spec'
Layout/LineLength:
Max: 80
Metrics/BlockNesting:
Enabled: false
Metrics/ParameterLists:
Enabled: false
Naming/FileName:
Enabled: false
Metrics/CyclomaticComplexity:
Enabled: false
Metrics/AbcSize:
Enabled: false
Metrics/ModuleLength:
Enabled: false
Metrics/MethodLength:
Enabled: false
Metrics/ClassLength:
Enabled: false
Metrics/BlockLength:
Enabled: false
Style/FrozenStringLiteralComment:
EnforcedStyle: never
Style/LineEndConcatenation:
Enabled: false
Style/StringConcatenation:
Enabled: false
Style/TrailingCommaInArrayLiteral:
EnforcedStyleForMultiline: comma
Style/TrailingCommaInHashLiteral:
EnforcedStyleForMultiline: comma
Style/HashSyntax:
EnforcedStyle: hash_rockets
Style/PercentLiteralDelimiters:
PreferredDelimiters:
default: '{}'
'%i': '{}'
'%I': '{}'
'%w': '{}'
'%W': '{}'
'%r': '{}'
Style/TrailingCommaInArguments:
EnforcedStyleForMultiline: comma
Style/Documentation:
Enabled: false
Metrics/PerceivedComplexity:
Enabled: false
Layout/DotPosition:
EnforcedStyle: trailing
sugarjar-4.0.0/.sugarjar.yaml 0000664 0000000 0000000 00000000122 15236151503 0016105 0 ustar 00root root 0000000 0000000 on_push: [lint]
lint_list_cmd: scripts/get_linters
unit:
- scripts/run_rspec.sh
sugarjar-4.0.0/CHANGELOG.md 0000664 0000000 0000000 00000016411 15236151503 0015146 0 ustar 00root root 0000000 0000000 # SugarJar Changelog
## 4.0.0 (2026-08-09)
* Rework config file format to be more friendly to working with multiple forges
* Add `modernizeconfig` command to help users migrate to new config file format
* Various improvements to gitlab compatibility
* Fix bugs around repo name detection when directory name doesn't match
* Revamp `help` to be easier to read and navigate
* Add support for Forgejo/Codeberg forges
* Update repoconfig format to allow specifying names for checks
## 3.0.1 (2026-06-19)
* Update tests for more resiliency in various package-building environments
* Better fallbacks for forge type
* Add config option to disable fork-based flows
* Fix false-positive for "primary branch doesn't match" error
* gitlab: Handle 3-level repos better
* gitlab: Fix `smartpullrequest` issues
* gitlab: significant improvement to `smartclone`
* Add `--fork-name` option to enable forking to a different repo name
## 3.0.0 (2026-06-08)
* Add support for GitLab
* Add release-branch handling
* Fixes to `sync`/`fsync` to not leave repo in consistent state
* When in manual lint/unit, allow user to skip amending, and keep testing
* Fix typos in various messages
## 2.0.2 (2026-01-08)
* Fix `branchclean` logic to properly compare with the target branch
(might have refused to clean branches that could be cleaned)
* Add new commands to handle remote branch cleanup as well as rename
`bclean` (keeping backwards compatible aliases):
* `localbranchclean` / `lbclean` - local branch clean. aliased as
`bclean` for back-comat
* `localbranchcleanall` / `lbcleanall` - local all branch clean aliased
as `bcleanall` for back-compat
* `remotebranchclean` / `rbclean` - remote branch clean
* `remotebranchcleanall` / `rbcleanall` - remote all branch clean
* `globalbranchclean` / `gbclean` - local+remote branch clean
* `globalbranchcleanall` / `gbcleanall` - local+remote all branch clean
* Added new `sync` command to aid syncing branches across multiple workstations,
see help for details.
* Fix meta-ref handling which fixes crashes when using `smartlog` during rebases
* Handle worktress gracefully when doing branch cleans
* Make unittests work properly outside of git repos
## 2.0.1 (2025-05-12)
* Fix gemspec to include new library files
## 2.0.0 (2025-05-11)
* Fix smartlog when on detached head
* Drop support for `hub`, and thus also `fallthru` mode
* Fix GHE handling when using `gh`
* Support `github_host` and `github_user` in repoconfig
* Replace `version` subcommand with `debuginfo` subcommand (`--version` still
exists)
* `smartclone`: set upstream for main branch to upstream remote when applicable
* Warn when deprecated options found in config file
* Fix handling of `--color` in some cornercases
* `subfeature` PRs: Fix bug where we would incorrectly deterine base branch
* Checks: Fix bug where we would lint even if repo was dirty causing confusing
output
* `feature` prefixes: Fix bug where we didn't look for the prefix on the base
branch when specified
* Better handle creating PRs to branches other than "main"
* Significantly improve unittest coverage
* Bump required Ruby to 3.2
## 1.1.3 (2025-02-20)
* smartpullrequest: When working with `gh`, bypass its attempt to push,
bypassing unnecessary prompts and branch track mangling
* smartpullrequest: Better support for autofill
* smartpullrequest: Don't attempt to stack when in forked repo
## 1.1.2 (2024-04-25)
* Add support for 'subfeatures'
* Add support for building stacked PRs based on 'subfeatures'
* smartpullrequest: only autofill in the PR when a single commit exists between
the base and us
* smartpullrequest: Add `--fill` option to let people opt-out of autofilling the
PR
* smartpullrequest: State that we're autofilling the PR when we do
* feature: Fix some corner cases where feature-prefixing didn't work
* pullsuggestions: Print the diff in the correct order
* feature/subfeature: set tracked branch for the user
* subfeature: automatically update tracked branch when previous tracked branch
disappears
## 1.1.1 (2024-02-12)
* Relax ruby requirements to allow for easier packaging
* Handle aborted rebases better
* Add bash-completion script
* Various doc updates
## 1.1.0 (2023-12-31)
* Fix include path for unittests for downstream packagers
* Bump ruby min versions
* Include Gemfile.lock for downstream packagers
## 1.0.1 (2023-12-20)
* `co` support for featureprefix
* Add `include_from` and `overwrite_from` support to repoconfig
* Support relative paths for lints/units
* `smartpr` now uses `--fill`
## 1.0.0 (2023-10-22)
* Add new "feature prefix" feature
* Implement `auto` setting for `github_cli`, default to `gh`
* Point people to Sapling
* Handle `sclone` of repos in personal orgs
* Better error when a subcommand isn't specified
* Various documentation fixes
## 0.0.11 (2022-10-06)
* Properly handle slashes in branch names (closes #101)
* Support for running a command to determine checks (linters, units) to run
* Support for using `gh` CLI instead of `hub` (experimental)
* Add new `pullsuggestions` command to pull in (accepted) suggestions from a
GitHub code review.
* Detect mismatched primary branch names to assist with projects changing from
`master` to `main`
## 0.0.10 (2021-12-06)
* Support 'main' as a default/primary branch
* Fix doc errors
* Handle rebase failures more gracefully, give users hints (closes #88)
* Handle SAML errors better (closes #95)
* Don't parse option args as subcommands (closes #89)
## 0.0.9 (2021-02-20)
* Fix smartclone not honoring `--github-host`
* Use SSH protocol by default on short repo names
* Handle anonymous auth failures gracefully
* Better support for autocorrecting linters
## 0.0.8 (2020-12-16)
* Colorize and simplify output
* New smartlog feature
* Doc fixes
## 0.0.7 (2020-11-23)
* Add new command `smartpullrequest` (or `smartpr` or `spr`) for creating
pull requests (closes #51)
* Add checks for dirty repos before `smartpush`, `forcepush`, and
`smartpullrequest`
* Add `--ignore-dirty` and `--ignore-prerun-failure` options
* Handle when git prompts for a username (closes #52)
* Always use SSH for the forked remote (closes #56)
* Better handling of various forms of repo URLs
* Fix typo of `version` in help message
* Fix typos in `README.md`
## 0.0.6 (2020-07-05)
* Add automatic commit template configuration (closes #38)
* bcleanall: Return to reasonable branch (fixes #37)
* Handle case where `hub` has no auth token (fixes #39)
* Fix crash in `smartclone`
* Improve logging
* Fix `sj unit` running lints instead of units
## 0.0.5 (2020-06-24)
* Fix global config file handling
* Better logging around lint/unit failuers
* Handle incorrect tracked branches better
## 0.0.4 (2020-06-17)
* Fix gemspec to include executables
* Add support for building omnibus releases
## 0.0.3 (2020-06-08)
* Stop rescuing NoMethodError (fixing a variety of confusing error cases)
* Fix crash when no `on_push` entry is in repo config
* Document contribution process (`CONTRIBUTING.md`)
* Document code of conduct (`CODE_OF_CONDUCT.md`)
## 0.0.2 (2020-06-06)
* Fix 'co' not accepting multiple arguments/options
* Fix README typos (#10, #11)
* Don't assume the ruby to run under
* Don't crash when no subcommands are passed in
* Don't assume paths (e.g. for hub, git)
* Fix crash for unknown method
* fix handling of empty config files
## 0.0.1 (2020-06-05)
* Initial release
sugarjar-4.0.0/CODE_OF_CONDUCT.md 0000664 0000000 0000000 00000006424 15236151503 0016137 0 ustar 00root root 0000000 0000000 # Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age,
body size, disability, ethnicity, sex characteristics, gender identity and
expression, level of experience, education, socio-economic status, nationality,
personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at phil@ipom.com. All complaints will
be reviewed and investigated and will result in a response that is deemed
necessary and appropriate to the circumstances. The project team is obligated
to maintain confidentiality with regard to the reporter of an incident. Further
details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor
Covenant](https://www.contributor-covenant.org), version 1.4, available
[here](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html)
For answers to common questions about this code of conduct, see [Contributor
Covenant](https://www.contributor-covenant.org)
sugarjar-4.0.0/CONTRIBUTING.md 0000664 0000000 0000000 00000001566 15236151503 0015573 0 ustar 00root root 0000000 0000000 # Contributing to SugarJar
We welcome contributions! Contributions come in a variety of forms: clear bug
reports, code, or spreading the word about this project.
If you'd like to contribute code, here's how.
Simply use SugarJar to make a fork and setup your repo:
```shell
sj sclone jaymzh/sugarjar
```
Make a branch for your change:
```shell
sj feature mychange
```
Make whatever changes you want, commit with a clear commit message, and a DCO.
We require [Developer Certificate of Origin
(DCO)](https://developercertificate.org/) via a 'signed-off-by:` line in your
commit (the `git commit -s` does this for you). The Chef community has a lot of
great documentation on this which you can find
[here](https://docs.chef.io/community_contributions/#developer-certification-of-origin-dco).
```shell
git commit -as
```
Make a pull request:
```shell
sj spush
sj pull-request
```
sugarjar-4.0.0/Gemfile 0000664 0000000 0000000 00000000172 15236151503 0014625 0 ustar 00root root 0000000 0000000 source 'https://rubygems.org'
gem 'sugarjar', :path => '.'
group :test do
gem 'mdl'
gem 'rspec'
gem 'rubocop'
end
sugarjar-4.0.0/Gemfile.lock 0000664 0000000 0000000 00000004324 15236151503 0015557 0 ustar 00root root 0000000 0000000 PATH
remote: .
specs:
sugarjar (4.0.0)
deep_merge
diffy
mixlib-log
mixlib-shellout
pastel
GEM
remote: https://rubygems.org/
specs:
ast (2.4.3)
chef-utils (19.3.15)
concurrent-ruby
concurrent-ruby (1.3.8)
deep_merge (1.2.2)
diff-lcs (1.6.2)
diffy (3.4.4)
ffi (1.17.4)
ffi (1.17.4-arm64-darwin)
ffi (1.17.4-x86_64-darwin)
ffi (1.17.4-x86_64-linux-gnu)
json (2.21.2)
kramdown (2.5.2)
rexml (>= 3.4.4)
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
language_server-protocol (3.17.0.6)
lint_roller (1.1.0)
mdl (0.18.0)
kramdown (~> 2.5)
kramdown-parser-gfm (~> 1.1)
mixlib-cli
mixlib-config
mixlib-shellout
uri
mixlib-cli (2.1.8)
mixlib-config (3.0.27)
tomlrb
mixlib-log (3.2.3)
ffi (>= 1.15.5)
mixlib-shellout (3.4.10)
chef-utils
parallel (2.1.0)
parser (3.3.12.0)
ast (~> 2.4.1)
racc
pastel (0.8.0)
tty-color (~> 0.5)
prism (1.9.0)
racc (1.8.1)
rainbow (3.1.1)
regexp_parser (2.12.0)
rexml (3.4.4)
rspec (3.13.2)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.6)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.8)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.7)
rubocop (1.89.0)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (>= 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.49.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.50.0)
parser (>= 3.3.7.2)
prism (~> 1.7)
ruby-progressbar (1.13.0)
tomlrb (2.0.4)
tty-color (0.6.0)
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.2.0)
uri (1.1.1)
PLATFORMS
arm64-darwin
ruby
x86_64-darwin
x86_64-linux
DEPENDENCIES
mdl
rspec
rubocop
sugarjar!
BUNDLED WITH
2.6.4
sugarjar-4.0.0/LICENSE 0000664 0000000 0000000 00000026127 15236151503 0014347 0 ustar 00root root 0000000 0000000 Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2020-present Phil Dibowitz
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
sugarjar-4.0.0/README.md 0000664 0000000 0000000 00000053314 15236151503 0014617 0 ustar 00root root 0000000 0000000 # SugarJar
[](https://github.com/jaymzh/sugarjar/actions?query=workflow%3ALint)
[](https://github.com/jaymzh/sugarjar/actions?query=workflow%3AUnittests)
[](https://github.com/jaymzh/sugarjar/actions?query=workflow%3A%22DCO+Check%22)
[](https://github.com/jaymzh/sugarjar/actions/workflows/codeql.yml)
Welcome to SugarJar - a git + github/gitlab helper. The only requirements are
Ruby, `git`, and either [gh](https://cli.github.com/) or
[glab](https://docs.gitlab.com/cli/), depending on which forge you are using.
SugarJar is inspired by [arcanist](https://github.com/phacility/arcanist), and
its replacement at Facebook, JellyFish. Many of the features they provide for
the Phabricator workflow this aims to bring to the GitHub workflow.
In particular there are a lot of helpers for using a squash-merge workflow that
is poorly handled by the standard toolsets.
If you miss Mondrian or Phabricator - this is the tool for you!
If you don't, there's a ton of useful stuff for everyone!
Jump to what you're most interested in:
> [!NOTE]
> There is a new 'modernizeconfig' option to automatically convert your
> config for the significant changes in 4.x.
* [Common Use-cases](#common-use-cases)
* [Auto Cleanup Squash-merged branches](#auto-cleanup-squash-merged-branches)
* [Smarter clones and remotes](#smarter-clones-and-remotes)
* [Work with stacked branches more easily](
#work-with-stacked-branches-more-easily)
* [Creating Stacked PRs with subfeatures](
#creating-stacked-prs-with-subfeatures)
* [Smart release branch handling](
#smart-release-branch-handling)
* [Have a better lint/unittest experience!](
#have-a-better-lintunittest-experience)
* [Better push defaults](#better-push-defaults)
* [Cleaning up your own history](#cleaning-up-your-own-history)
* [Better feature branches](#better-feature-branches)
* [Smartlog](#smartlog)
* [Sync work across workstations](#sync-work-across-workstations)
* [Pulling in suggestions from the web](#pulling-in-suggestions-from-the-web)
* [And more!](#and-more)
* [Installation](#installation)
* [Configuration](#configuration)
* [Repository Configuration](#repository-configuration)
* [Commit Templates](#commit-templates)
* [Enterprise GitHub](#enterprise-github)
* [FAQ](#faq)
## Common Use-cases
### Auto cleanup squash-merged branches
Clean-up squash-merged PRs safely and easily
It is common for a PR to go back and forth with a variety of nits, lint fixes,
typos, etc. that can muddy history. So many projects will "squash and merge"
when they accept a pull request. However, that means `git branch -d
BRANCH` doesn't work. Git will tell you the branch isn't fully merged.
You can, of course `git branch -D BRANCH`, but that does no safety
checks at all, it forces the deletion.
Enter `sj lbclean` - it determines if the contents of your branch has been merge
and safely deletes if so. (Note: `lbclean` stands for "local branch clean", and
is aliased to `bclean` for both backwards-compatibility and also since it's the
most common branch-cleanup command).

Will delete a branch, if it has been merged, **even if it was squash-merged**.
You can pass it a branch if you'd like (it defaults to the branch you're on):
`sj bclean BRANCH`.
But it gets better! You can use `sj bcleanall` to remove all branches that have
been merged:

*NOTE*: You can add long-lived release-branches to your RepoConfig to prevent
cleaning; see [Smart release branch handling](️#smart-release-branch-handling).
There is also `sj rbclean` ("remote branch clean") (and `sj rbcleanall`) for
cleanup of remote branches. *Note*: This cannot differentiate between
PR/feature branches which have been merged and long-lived release branches that
have been merged (e.g. if '2.0-release' is a branch and has no commits not in
main, it will be deleted).
There is even `sj gbclean` ("global branch clean") (and `sj gbcleanall`) which
will do both the local and remote cleaning.
*NOTE*: Remote branch cleaning is still experimental, use with caution!
### Smarter clones and remotes
Fork, clone, and add the upstream remote automatically
There's a pattern to every new repo we want to contribute to. First we fork,
then we clone the fork, then we add a remote of the upstream repo. It's
monotonous. SugarJar does this for you:

`sj` accepts both `smartclone` and `sclone` for this command.
This will:
* Fork the repo to your personal org (if you don't already have a fork,
and if it's not already in your personal org)
* Clone your fork
* Add the original as an 'upstream' remote
Note that if you pass in a full-formed URL (like
`git@github.com:chef/omnibus-toolchain.git`), SugarJar will infer the host and
the forge type (gitub, gitlab, etc.). However, it does accept short names for
repos ($org/$repo) - however, for cloning, since there is no repo yet for
SugarJar to determine the forge from, you will need either to specify
`--default-forge-host`, or have `default_forge_host` set in your config file.
Like `git clone`, `sj smartclone` will accept an additional argument as the
destination directory to clone to. It will also pass any other unknown options
to `git clone` under the hood.
If you don't work with fork-based workflows, you can set `use_forks: false`
in your config for the right host:
```yaml
host_configs:
default:
use_forks: false
```
See [Configuration](#Configuration) for more details on the config file.
### Work with stacked branches more easily
Create and manage stacked/dependent branches easily
It's important to break changes into reviewable chunks, but working with
stacked branches can be confusing. SugarJar provides several tools to make this
easier.
First, and foremost, is `feature` and `subfeature`. Regardless of stacking, the
way to create a new feature bracnh with sugarjar is with `sj feature` (or `sj
f` for short):

A "feature" in SugarJar parlance just means that the branch is always created
from "most main" - this is usually `upstream/main`, but SJ will figure out
which remote is the "upstream", even if it's `origin`, and then will determine
the primary branch (`main` or for older repos `master`). It's also smart enough
to fetch that remote first to make sure you're working on the latest HEAD.
When you want to create a stacked PR, you can create `subfeature`, which, at
its core is just a branch created from the current branch:

If you create branches like this then sugarjar can now make several things
much easier:
* `sj up` will rebase intelligently
* After an `sj bclean` of a branch earlier in the tree, `sj up` will update
the tracked branch to "most main"
There are two commands that will show you the state of your stacked branches:
* `sj binfo` - shows the current branch and its ancestors up to your primary
branch
* `sj smartlog` (aka `sj sl`) - shows you the whole tree.
To continue with the example above, my `smartlog` might look like:

As you can see, `mynewthing` is derived from `main`, and `dependentnewthing` is
derived from `mynewthing`.
Now lets make a different feature stack:

The `smartlog` will now show us this tree, and it's a bit more interesting:

Here we can see from `main`, we have two branches: one going to `mynewthing`
and one going to `anotherfeature`. Each of those has their own dependent branch
on top.
Now, what happens if I make a change to `mynewthing` (the bottom of the first
stack)?

We can see here now that `dependentnewthing`, is based off a commit that _used_
to be `mynewthing` (`5086ee`), but `mynewthing` has moved. Both `mynewthing`
and `dependentnewthing` are derived from `5086ee` (the old `mynewthing`), but
`dependentnewthing` isn't (yet) based on the current `mynewthing`. But SugarJar
will handle this all correctly when we ask it to update the branch:

Here we see that SugarJar knew that `dependentnewthing` should be rebased onto
`mynewthing`, and it did the right thing - from main there's still the
`50806ee` _and_ the new additional change which are now both part of the
`mynewthing` branch, and `dependentnewthing` is based on that branch, this
including all 3 commits in the right order.
Now, lets say that `mynewthing` gets merged and we use `bclean` to clean it all
up, what happens then?

SugarJar detects that branch is gone and thus this branch should now be based
on the upstream main branch!
### Creating Stacked PRs with subfeatures
Automatically base PRs for subfeatures on their parent branch
When dependent branches are created with `subfeature`, when you create a PR,
SugarJar will automatically set the 'base' of the PR to the parent branch. By
default it'll prompt you about this, but you can set `pr_autostack` to `true`
in your config to tell it to always do this (or `false` to never do this):
```text
$ sj spr
Autofilling in PR from commit message
It looks like this is a subfeature, would you like to base this PR on mynewthing? [y/n] y
...
```
### Smart release branch handling
Configure and protect long-lived release branches
You can tell sugar what release branches exist, and it will intelligently
handle them. So of you specify, in your repoconfig:
```yaml
release_branches: ['v1-branch', 'v2-branch']
```
Then:
* `sj feature v1-backport-foo v2-branch` will automatically base this
branch on `upstream/v2-branch` (or `origin/v2-branch` as appropriate)
* `sj feature v2-branch` will checkout `v2-branch` with the upstream set
to `upstream/v2-branch` (or `origin/v2-branch` as appropriate)
* `sj lbclean`/`sj lbcleanall` (of all varieties) will never reap release
branches
### Have a better lint/unittest experience!
Run lint and unittests automatically before pushing
Ever made a PR, only to find out later that it failed tests because of some
small lint issue? Not anymore! SJ can be configured to run things before
pushing. For example,in the SugarJar repo, we have it run Rubocop (ruby lint)
and Markdownlint `on_push`. If those fail, it lets you know and doesn't push.
You can configure SugarJar to tell it how to run both lints and unittests for
a given repo and if one or both should be run prior to pushing.
The details on the config file format is below, but we provide three commands:
```shell
sj lint
```
Run all linters.
```shell
sj unit
```
Run all unittests.
```shell
sj smartpush # or spush
```
Run configured push-time actions (nothing, lint, unit, both), and do not
push if any of them fail.
### Better push defaults
Smart defaults for `smartpush`
In addition to running pre-push tests for you `smartpush` also picks smart
defaults for push. So if you `sj spush` with no arguments, it uses the
`origin` remote and the same branch name you're on as the remote branch.
### Cleaning up your own history
Safely amend commits and force-push with `amend`/`fpush`
Perhaps you contribute to a project that prefers to use merge commits, so you
like to clean up your own history. This is often difficult to get right - a
combination of rebases, amends and force pushes. We provide two commands here
to help.
The first is pretty straight forward and is basically just an alias: `sj
amend`. It will amend whatever you want to the most recent commit (just an
alias for `git commit --amend`). It has a partner `qamend` (or `amendq` if you
prefer) that will do so without prompting to update your commit message.
So now you've rebased or amended, pushing becomes challenging. You can `git push
--force`, but everyone knows that's incredibly dangerous. Is there a better
way? There is! Git provides `git push --force-with-lease` - it checks to make
sure you're up-to-date with the remote before forcing the push. But man that
command is a mouthful! Enter `sj fpush`. It has all the smarts of `sj
smartpush` (runs configured pre-push actions), but adds `--force-with-lease` to
the command!
### Better feature branches
Create feature branches with the right base and prefix
When you want to start a new feature, you want to start developing against
latest. That's why `sj feature` defaults to creating a branch against what we
call "most master". That is, `upstream/master` if it exists, otherwise
`origin/master` if that exists, otherwise `master`. You can pass in an
additional argument to base it off of something else.
```shell
$ git branch
master
test1
test2
* test2.1
test3
$ sj feature test-branch
Created feature branch test-branch based on origin/master
$ sj feature dependent-feature test-branch
Created feature branch dependent-feature based on test-branch
```
Additionally you can specify a `feature_prefix` in your config which will cause
`feature` to create branches prefixed with your `feature_prefix` and will also
cause `co` to checkout branches with that prefix. This is useful when
organizations use branch-based workflows and branches need to be prefixed with
e.g. `$USER/`.
For example, if your prefix was `user/`, then `sj feature foo` would create
`user/foo`, and `sj co foo` would switch to `user/foo`.
Here's an example:
```yaml
host_configs:
default:
feature_prefix: "jsmith/"
```
### Smartlog
Visualize your branch tree
Smartlog will show you a tree diagram of your branches! Simply run `sj
smartlog` or `sj sl` for short.

### Sync work across workstations
Keep branches in sync across multiple machines
If you work on multiple workstations, keeping your branches in-sync can be a
pain. SugarJar provides `sync` to help with this.
For example, if you do some work on feature `foo` on machine1 and push to
`origin/foo` (intending to eventually merge to `upstream/main`), then on
machine2, you pull that branch, do more work, which you also push to
`origin/foo`, then on machine1, you can do `sj sync` to pull down the changes
from `origin/foo`. If you have local changes, that are not already on
`origin/foo`, those will be rebased on top of the changes from `origin/foo`.
It's very similar to `sj up`, but instead of rebasing on top of the tracking
branch, it rebases on top of the push target branch.
### Pulling in suggestions from the web
Merge in GitHub/GitLab suggested changes
When someone 'suggests' a change in the GH/GL WebUI, once you choose to commit
them, your origin and local branches are no longer in-sync. The
`pullsuggestions` command will attempt to merge in any remote commits to your
local branch. This command will show a diff and ask for confirmation before
attempting the merge and - if allowed to continue - will use a fast-forward
merge.
### Modernize your config
Convert your old config to the new `host_configs` format
In 4.0, we significantly revamped how the configuration works, adding the new
`host_configs` section, and deprecating or moving many top-level configs. In
order to make this transition easier, there is now a `modernizeconfig` command
to convert it for you!
```shell
sj modernizeconfig ~/.config/sugarjar/config.yml
```
This will generate a new config for you, show you a diff, and offer to either
update the existing file, or save it off the side for you.
### And more!
There's even more than this!
See `sj help` for more commands!
## Installation
Sugarjar is packaged in a variety of Linux distributions - see if it's on the
list here, and if so, use your package manager (or `gem`) to install it:
[](https://repology.org/project/sugarjar/versions)
If you are using a Linux distribution version that is end-of-life'd, click the
above image, it'll take you to a page that lists unsupported distro versions
as well (they'll have older SugarJar, but they'll probably still have some
version).
**Ubuntu users**: You can use [this PPA](
https://launchpad.net/~michel-slm/+archive/ubuntu/sugarjar) to get newer
versions for all supported Ubuntu releases (as well as some older versions).
Ubuntu package maintainer.
**MacOS users**: We recommend using Homebrew - we keep SugarJar updated in
Homebrew Core.
Finally, if none of those work for you, you can clone this repo and run it
directly from there.
## Configuration
Sugarjar will read in both a system-level config file
(`/etc/sugarjar/config.yaml`) and a user-level config file
(`~/.config/sugarjar/config.yaml`), if they exist. Anything in the user config
will override the system config, and command-line options override both. The
yaml file is a straight key-value pair of options without their '--'.
See [examples/sample_config.yaml](examples/sample_config.yaml) for an example
configuration file.
In addition, the environment variable `SUGARJAR_LOGLEVEL` can be defined to set
a log level. This is primarily used as a way to turn debug on earlier in order
to troubleshoot configuration parsing.
Deprecated fields will cause a warning, but you can suppress that warning by
defining `ignore_deprecated_options`, for example:
```yaml
old_option: foo
ignore_deprecated_options:
- old_options
```
## Repository Configuration
Sugarjar looks for a `.sugarjar.yaml` in the root of the repository to tell it
how to handle repo-specific things. See
[examples/sample_repoconfig.yaml](examples/sample_repoconfig.yaml) for an
example configuration that walks through all valid repo configurations in
detail.
### Commit Templates
While GitHub provides a way to specify a pull-request template by putting the
right file into a repo, there is no way to tell git to automatically pick up a
commit template by dropping a file in the repo. Users must do something like:
`git config commit.template `. Making each developer do this is error
prone, so this setting will automatically set this up for each developer.
## Enterprise GitHub/GitLab
Like `gh` and `glab`, SugarJar supports Enterprise versions of GitHub and
GitLab. In fact, we provide extra features just for it.
By default, SugarJar will pick up the appropriate information from the repo
itself, so the only real interesting part is `smartclone`. In most cases,
SugarJar will automatically determine the proper forge-host based on the URL
you pass in. However, if you use short names ("repo/org" or
"company/repo/org"), then you can either pass in `--default-forge-host`, or, if
you want to always default to a given host, you can set a top-level
`default_forge_host` in your SugarJar config:
So, for example you might have:
```yaml
default_forge_host: gh.sample.com
```
## FAQ
**Why the name SugarJar?**
It's mostly a backronym. Like jellyfish, I wanted two letters that were on home
row on different sides of the keyboard to make it easy to type. I looked at the
possible options that where there and not taken and tried to find one I could
make an appropriate name out of. Since this utility adds lots of sugar to git
and github/gitlab, it seemed appropriate.
**I'd like to package SugarJar for my favorite distro/OS, is that OK?**
Of course! But I'd appreciate you emailing me to give me a heads up. Doing so
will allow me to make sure it shows up in the Repology badge above.
**What platforms does it work on?**
Since it's Ruby, it should work across all platforms, however, it's developed
and primarily tested on Linux as well as regularly used on Mac. I've not tested
it on Windows, but I'll happily accept patches for Windows compatibility.
**How do I get tab-completion?**
If the package for your OS/distro didn't set it up automatically, you should
find that `sugarjar_completion.bash` is included in the package, and you can
simply source that in your dotfiles, assuming you are using bash.
**What happens now that Sapling is released?**
SugarJar isn't going anywhere. This was meant to replace arc/jf, which has now
been open-sourced as [Sapling](https://sapling-scm.com/), so I highly recommend
taking a look at that!
Sapling is a great tool and solves a variety of problems SugarJar will never be
able to. However, it is a significant workflow change that won't be appropriate
for all users or use-cases. Similarly there are workflows and tools that
Sapling breaks. Further, we support some things Sapling does not. So worry not,
SugarJar will continue to be maintained and developed.
sugarjar-4.0.0/RELEASE_PROCESS.md 0000664 0000000 0000000 00000001332 15236151503 0016131 0 ustar 00root root 0000000 0000000 # Rolling a release
## Prep the release
* Update version number in `lib/sugarjar/version.rb`
* Update the `CHANGELOG.md`
* Create a PR, get it merged
## Tag the release
* version='a.b.c'
* Add a tag: `git tag -a v${version?} -m "version ${version?}" -s`
* Push the tag: `git push origin --tags`
## Publish a gem
* Build a gem: `gem build sugarjar.gemspec`
* Push the gem: `gem push sugarjar-${version?}.gem`
## Publish GH Release
Go to release, add new one.
## Publish Fedora builds
See [packaging/README-fedora.md](packaging/README-fedora.md).
## Notify Debian/Ubuntu packager
Ping Michel Lind
## Update Homebrew
See [packaging/README-brew.md](packaging/README-brew.md).
## Notify AUR packager
Ping Zeal Wierslee
sugarjar-4.0.0/bin/ 0000775 0000000 0000000 00000000000 15236151503 0014102 5 ustar 00root root 0000000 0000000 sugarjar-4.0.0/bin/sj 0000775 0000000 0000000 00000015664 15236151503 0014460 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby
# SugarJar
require 'optparse'
require 'mixlib/shellout'
require_relative '../lib/sugarjar/commands'
require_relative '../lib/sugarjar/config'
require_relative '../lib/sugarjar/help'
require_relative '../lib/sugarjar/log'
require_relative '../lib/sugarjar/util'
require_relative '../lib/sugarjar/version'
SugarJar::Log.level = Logger::INFO
# Don't put defaults here, put them in SugarJar::Config - otherwise
# these defaults overwrite whatever is in config files.
options = {}
# If ENV['SUGARJAR_DEBUG'] is set, it overrides the config file,
# but not the command line options, so set that one here. Also
# start the logger at that level, in case we are debugging option loading
# itself
if ENV['SUGARJAR_LOGLEVEL']
options['log_level'] = SugarJar::Log.level = ENV['SUGARJAR_LOGLEVEL'].to_sym
end
options['_cli_overrides'] = {}
parser = OptionParser.new do |opts|
opts.banner = 'Usage: sj [] []'
opts.separator ''
opts.separator 'Command, args, and options, can appear in any order.'
opts.separator ''
opts.separator 'OPTIONS:'
opts.on(
'--default-forge-host HOST',
'The default host of your forge (github, gitlab, etc.) when it cannot ' +
'be determined automatically. In nearly every case SJ will ' +
'automatically determine this. However, for `smartclone`, if ' +
'you use shortnames (e.g. org/repo) that do not contain a host, this ' +
'is the default to assume. Most useful in your config file when set ' +
'to your most comomn forge host (e.g. github.com)',
) do |host|
options['default_forge_host'] = host
end
opts.on(
'--feature-prefix PREFIX',
'Bypass the config and just use PREFIX.',
) do |prefix|
options['_cli_overrides']['feature_prefix'] = prefix
end
opts.on(
'--forge-type TYPE',
'GENERALLY NOT NEEDED. In case SJ cannot detect the type of a forge ' +
'from the hostname during smartclone. Only available in CLI, not a ' +
'config file option. Forge type: github, gitlab.',
) do |type|
options['_cli_overrides']['forge_type'] = type
end
opts.on(
'--forge-user USER',
'Bypass the config and just use USER as the forge user.',
) do |user|
options['_cli_overrides']['user'] = user
end
opts.on('-h', '--help', 'Print this help message') do
puts opts
exit
end
opts.on(
'--ignore-dirty',
'Tell command that check for a dirty repo to carry on anyway. ' +
'[default: false]',
) do
options['ignore_dirty'] = true
end
opts.on(
'--ignore-prerun-failure',
'Ignore preprun failure on *push commands. [default: false]',
) do
options['ignore_prerun_failure'] = true
end
opts.on(
'--log-level LEVEL',
'Set logging level (fatal, error, warning, info, debug, trace). This can ' +
'also be set via the SUGARJAR_LOGLEVEL environment variable. [default: ' +
'info]',
) do |level|
options['log_level'] = level
end
opts.on(
'--[no-]pr-autofill',
'When creating a PR, auto fill the title & description from the top ' +
'commit if we are using "gh". [default: true]',
) do |autofill|
options['pr_autofill'] = autofill
end
opts.on(
'--fork-name NAME',
'When forking a repo (in `smartclone`), fork the repo to a different ' +
'name. See the help for `smartclone` below.',
) do |val|
options['fork_name'] = val
end
opts.on(
'--[no-]pr-autostack',
'When creating a PR, if this is a subfeature, should we make it a ' +
'PR on the PR for the parent feature. If not specified, we prompt ' +
'when this happens, when true always do this, when false never do ' +
'this. Only applicable when usiing "gh" and on branch-based PRs.',
) do |autostack|
options['pr_autostack'] = autostack
end
opts.on('--[no-]color', 'Enable color. [default: true]') do |color|
options['color'] = color
end
opts.on('--version') do
puts SugarJar::VERSION
exit
end
opts.separator ''
opts.separator 'COMMANDS:'
opts.separator SugarJar::Help.summary_list
opts.separator ''
opts.separator "Run 'sj help ' for details on a specific command."
end
extra_opts = []
argv_copy = ARGV.dup
# We want to allow people to pass in extra args to be passed to commands (like
# `amend`), but OptionParser doesn't easily allow this. So we loop over it,
# catching exceptions.
begin
# HOWEVER, anytime it throws an exception, for some reason, it clears
# out all of ARGV, or whatever you passed to as ARGV.
#
# This not only prevents further parsing, but also means we lose
# any non-option arguements (like the subcommand!)
#
# So we save a copy, and if we throw an exception, save the option that
# caused it, remove that option from our copy, and then re-populate argv
# with what's left.
#
# By doing this we not only get to parse all the options properly and
# save unknown ones, but non-option arguements, which OptionParser
# normally leaves in ARGV stay in ARGV.
saved_argv = argv_copy.dup
parser.parse!(argv_copy)
rescue OptionParser::InvalidOption => e
SugarJar::Log.debug("Saving unknown argument #{e.args}")
extra_opts += e.args
# e.args is an array, but it's only ever one arguement per exception
saved_argv.delete(e.args.first)
argv_copy = saved_argv.dup
SugarJar::Log.debug(
"Continuing option parsing with remaining ARGV: #{argv_copy}",
)
retry
end
options = SugarJar::Config.config.merge(options)
SugarJar::Log.level = options['log_level'].to_sym if options['log_level']
subcommand = argv_copy.reject { |x| x.start_with?('-') }.first
if ARGV.empty? || !subcommand
puts parser
exit
end
SugarJar::Log.debug("Final config: #{options}")
# if the command is help, we don't bother to create the Commands obj
if subcommand == 'help'
help_target = argv_copy.reject { |x| x.start_with?('-') }[1]
if help_target
help_text = SugarJar::Help.command_help(help_target)
if help_text
puts help_text
else
SugarJar::Log.fatal("No such subcommand: #{help_target}")
exit 1
end
else
puts parser
end
exit
end
sj = SugarJar::Commands.new(options)
valid_commands = sj.public_methods - Object.public_methods
is_valid_command = valid_commands.include?(subcommand.to_sym)
# We can't do .delete(subcommand) because someone could, for example
# have a branch called 'co' and then do 'sj co co' - which will then
# remove _all_ instances of 'co'. So find the first instance and remove
# that.
argv_copy.delete_at(argv_copy.find_index(subcommand))
SugarJar::Log.debug("subcommand is #{subcommand}")
# Extra options we got, plus any left over arguements are what we
# pass to Commands so they can be passed to git as necessary
extra_opts += argv_copy
SugarJar::Log.debug("extra unknown options: #{extra_opts}")
extra_opts = [options] if subcommand == 'debuginfo'
unless is_valid_command
SugarJar::Log.fatal("No such subcommand: #{subcommand}")
exit 1
end
SugarJar::Log.debug(
"running #{subcommand}; extra opts: #{extra_opts.join(', ')}",
)
sj.send(subcommand.to_sym, *extra_opts)
sugarjar-4.0.0/examples/ 0000775 0000000 0000000 00000000000 15236151503 0015150 5 ustar 00root root 0000000 0000000 sugarjar-4.0.0/examples/sample_config.yaml 0000664 0000000 0000000 00000001654 15236151503 0020650 0 ustar 00root root 0000000 0000000 # This is a sample SugarJar config
#
# SugarJar will look for this config in:
#
# - /etc/sugarjar/config.yaml
# - ~/.config/sugarjar/config.yaml
#
# The latter will overwrite anything in the former.
#
# NOTE: This file does NOT document ALL options since any command-line option
# to SugarJar is a valid configuration in this file, so see `sj help` for full
# details.
# Autofill in my PRs from my commit message (default: true)
pr_autofile: true
# Auto stack PRs when subfeatures are detected (default is `nil`, which prompts,
# but use `true` or `false` to force an option without prompting)
pr_autostack: true
# Don't warn about deprecated config file options if they are in this
# list
ignore_deprecated_options: [ 'gh_cli' ]
host_configs:
default:
use_forks: true
github.com:
user: c00ldude
gitlab.com:
user: thisc00ldude
gitlab.company.com:
user: tsmith
use_forks: false
feature_prefix: "tsmith/"
sugarjar-4.0.0/examples/sample_repoconfig.yaml 0000664 0000000 0000000 00000006511 15236151503 0021533 0 ustar 00root root 0000000 0000000 # This is a sample `repoconfig` for SugarJar
#
# Configs should be named `.sugarjar.yaml` and placed in the root
# of your repository.
#
# `include_from` is a meta config wich will read from an additional
# configuration file and merge anything from the file onto whatever is in the
# primary file. This is helpful to have a repo configuration that applies to
# all/most developers, but allow individual developers to add to over overwrite
# specific configurations for themselves. If the file does not exist, this
# configuration is ignored.
include_from: .sugarjar_local.yaml
# `overwrite_from` is a meta config which works much like `include_from`,
# except that if the file is found, everything else in this configuration file
# will be ignored and the configuration will be entirely read from the
# referenced file. If the file does not exist, this configuration is ignored.
overwrite_from: .sugarjar_local_overwrite.yaml
# `release_branches` tells SugarJar several things:
# 1. These branches should not be repead when running `bclean`/`bcleanall`.
# 2. When a feature-branch is made from a release branch (e.g. `2.x-branch`),
# it will actually track the release branch's upstream
# (e.g. `upstream/2.x-bramch`), allowing it to work the same as as a
# feature made off of main.
release_branches:
- 2.x-branch
- 3.x-branch
# `lint` is a list of scripts to run when `sj lint` is executed (or, if
# configured, to run on `sj spush`/`sj fpush` - see `on_push` below).
# Regardless of where `sj` is run from, these scripts will be run from the root
# of the repo. If a slash is detected in the first 'word' of the command, it
# is assumed it is a relative path and `sj` will check that the file exists.
lint:
# can be a string which is just a command to run...
- scripts/run_rubocop.sh
# want it to have a pretty name? Entries can be a hash instead...
- name: mdl
command: scripts/run_mdl.sh
# `unit` is a list of scripts to run when `sj unit` is executed (or, if
# configured to run on `sj spush`/`sj fpush`- see `on_push` below). Regardless
# of where `sj` is run from, these scripts will be run from the root of the
# repo. If a slash is detected in the first 'word' of the command, it is
# assumed it is a relative path and `sj` will check that the file exists.
unit:
# like lint, supports bare strings...
- bundle exec rspec
# or hashes...
- name: unittests
command: scripts/run_tests.sh
# `lint_list_cmd` is like `lint`, except it's a command to run which will
# determine the proper lints to run and return them, one per line. This is
# useful, for example, when you want to only run lints relevant to the changed
# files.
lint_list_cmd: scripts/determine_linters.sh
# `unit_list_cmd` is like `unit`, except it's a command to run which will
# determine the proper units to run and return them, one per line. This is
# useful, for example, when you want to only run tests relevant to the changed
# files.
unit_list_cmd: scripts/determine_tests.sh
# `on_push` determines what checks should be run when pushing a repo. Valid
# options are `lint` and/or `unit` (or nothing, of course).
on_push: [lint] # or [lint, unit]
# `commit_template` points to a file to set the git `commit.template` config
# to. This is really useful for ensuring that everyone has the same
# template configured.
commit_template: .git_commit_template.txt
sugarjar-4.0.0/extras/ 0000775 0000000 0000000 00000000000 15236151503 0014640 5 ustar 00root root 0000000 0000000 sugarjar-4.0.0/extras/sugarjar_completion.bash 0000664 0000000 0000000 00000002707 15236151503 0021554 0 ustar 00root root 0000000 0000000 # bash completion for sugarjar
SJCONFIG="$HOME/.config/sugarjar/config.yaml"
_sugarjar_completions()
{
if [ "${#COMP_WORDS[@]}" -eq 2 ]; then
return
fi
local -a suggestions
# grap any and all feature_prefixes so that we can let the user ignore that
# part. If we have `yq` we'll use it as that's going to be always 100%
# reliable, but if we don't, do our best with shell utils
local prefixes=''
if [ -e "$SJCONFIG" ]; then
if type yq &>/dev/null; then
# We don't use the '// empty' syntax here as thats only in
# very new yq, so use the more standard '| select(. != null)'
yq_search='[.host_configs[].feature_prefix | select(. != null)] | unique[]'
prefixes=$(yq -r "$yq_search" $SJCONFIG | xargs)
else
prefixes=$(
grep feature_prefix $SJCONFIG | cut -f2 -d: | sort -u | xargs
)
fi
fi
case "${COMP_WORDS[1]}" in
co|checkout|bclean)
local branches=$(git branch | sed -e 's/* //g' | xargs)
if [ -n "$prefixes" ]; then
regex=$(printf '%s\n' $prefixes | paste -sd'|')
local branches=$(echo $branches | sed -E "s!($regex)!!g")
fi
suggestions=($(compgen -W "$branches" -- "${COMP_WORDS[2]}"))
COMPREPLY=("${suggestions[@]}")
;;
*)
return
esac
}
complete -F _sugarjar_completions sj
sugarjar-4.0.0/images/ 0000775 0000000 0000000 00000000000 15236151503 0014577 5 ustar 00root root 0000000 0000000 sugarjar-4.0.0/images/bclean.png 0000664 0000000 0000000 00000003264 15236151503 0016536 0 ustar 00root root 0000000 0000000 PNG
IHDR + lE sRGB, gAMA a cHRM z&