Jaspr creates and manages stacked pull requests on GitHub. Commits in your local branch are pushed as a stack of dependent pull requests, with one PR per commit. Commit subjects are used as PR titles, and commit bodies are used as PR descriptions. Reviewers see small, focused diffs, which eases code review and enables faster and more frequent merges, which are especially useful for teams practicing trunk-based development.
Because I like Gerrit and wish GitHub were Gerrit. But since it isn't, I have to use this tool instead. For more rationale see this excellent blog post.
Jaspr can be installed via Homebrew on macOS or Linux. Binaries are available currently for Linux x86_64 and macOS arm64. (Please open an issue if you are interested in Jaspr on another architecture.)
$ brew tap michaelsims/tap
$ brew update
$ brew install jasprIf this is your first time using Jaspr, you will need to generate a config file with jaspr init and update it with a GitHub Personal Access Token (classic) with the permissions read:org, read:user, repo, and
user:email.
Note that any option for any Jaspr command can be supplied via your global config file or your repo-specific config
file. For example, if you want to use --log-level=WARN, you don't have to
supply this to every command but can instead add it to your config file:
log-level=WARNSee the generated config file for the available options and descriptions.
Some commands to try:
$ jaspr --help
$ jaspr status
$ jaspr push
$ jaspr merge
$ jaspr auto-merge
$ jaspr cleanAny of the above can be invoked with --help.
Enjoy!
GitHub branch protection rules and rulesets are both complex features, and I won't pretend to be an expert. There are many configurations that are likely valid, so ultimately what works, works. But there are some considerations to keep in mind with regard to using Jaspr:
- At a minimum you should have a workflow that triggers on pull requests (either for all branches or at least for
mainand forjaspr/**/*) that runs checks and reports status back to GitHub. Likely you want a branch protection rule that requires status checks to pass before merging tomainat least. - If
git jaspr statusdoes not show the "pr approved" checkmark or x, you may need to create a branch protection rule that requires pull request approvals before merging first. (I've noticed this behavior is inconsistent... sometimes this bit shows even without this requirement.) - You want to make sure that people can force push to
jaspr/**/*. Jaspr's philosophy is that of Gerrit's, which is to favor a trunk-based workflow where PR remediation is done via amends/edits and is force-pushed (this is why Jaspr pushes revision history branches and includes diff links for them in the PR descriptions).
This repo contains functional tests that work with a real GitHub remote and actually open/modify/close PRs. To use these
tests, you will need to set some configuration options in your ~/.git-jaspr.properties file:
github-test-harness.githubUri=git@github.com:some-project.git
github-test-harness.userKey.michael.name=Michael Sims
github-test-harness.userKey.michael.email=michael@example.com
github-test-harness.userKey.michael.githubToken=<redacted>
github-test-harness.userKey.derelictMan.name=Frank Grimes
github-test-harness.userKey.derelictMan.email=frank@example.com
github-test-harness.userKey.derelictMan.githubToken=<redacted>
Replace the <redacted> with GitHub classic PATs with the same permissions required by Jaspr. The githubUri should be
the URI of a test project that the tests can push to and open PRs to, etc. The repo should have a workflow triggered on
PRs that runs a fake verify script:
Workflow:
name: Pull Request
on:
pull_request:
workflow_dispatch:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
name: Checkout repo
with:
fetch-depth: 0
- run: git checkout origin/${{ github.event.pull_request.head.ref }}
- name: Verify
run: |
chmod a+x verify.sh
./verify.sh
verify.sh:
#!/usr/bin/env bash
set -x
set -e
# Keeping around this commented code in case github decides to start being a pain again
#git log --graph --all
# shellcheck disable=SC2034
#gitOutput=$(git show --pretty=full)
verifyDelay=$(git show --pretty=full | awk '/^ verify-delay:/ { print $2 }')
verifyResult=$(git show --pretty=full | awk '/^ verify-result:/ { print $2 }')
if [[ "$verifyDelay" -gt 0 ]]; then
echo "Delaying $verifyDelay seconds"
sleep "$verifyDelay"
fi
if [[ "$verifyResult" ]]; then
echo "Exiting with code $verifyResult"
exit "$verifyResult"
else
echo "Verify result not found in commit body, exiting success by default"
exit 0
fiIf you try to run these and have questions, please contact me or open an issue, and I'll try to help.