gh-arc is a github cli extension that implements an opinionated trunk-based development workflow with Github, built upon the great traditions of tools like phacility/arcanist and phorge/arcanist.
Tip
Check out this DORA Trunk-based development page if, by any chance, this is the first time you're hearing about it and interested to learn more about about this methodology.
Install the latest release using the GitHub CLI:
gh extension install serpro69/gh-arcgit clone https://github.com/serpro69/gh-arc.git
cd gh-arc && go build . && gh extension install .Tip
Clone the repo to a permanent location, gh cli will alias the binary when you run gh extension install . command.
gh-arc requires GitHub CLI authentication with specific OAuth scopes to access user information and repository data. The required additional scopes are:
user:email- Access to user email addressesread:user- Read user profile data
If you're already logged in to GitHub CLI, refresh your token with the additional required scopes:
gh auth refresh --scopes "user:email,read:user"If you're not logged in, authenticate with the additional required scopes:
gh auth login --scopes "user:email,read:user"Alternatively, you can provide your own personal access token:
echo "your-token-here" | gh auth login --with-tokenNote
Personal access tokens must include the user:email and read:user scopes, in addition to the scopes needed by gh cli itself, to work with gh-arc.
To verify your authentication and scopes:
gh arc authgh-arc is a "wrapper" that sits on top of other tools: Github (naturally, being a github-cli extension) and Linear (if you use it for issue management instead of Github), but also linters, formatters, unit test frameworks and others. It provides a simple command-line API to manage code review and some related revision control operations.
For a detailed list of all available commands, run:
gh arc helpFor detailed information about a specific command, run:
gh arc help <command>In a gist, gh arc allows you to do things like:
- get detailed help about available commands with
gh arc help - create a new, short-lived, feature branch from an up-to-date
origin/HEADwithgh arc work - send your code to Github for review with
gh arc diff - show pending revision information with
gh arc list - find likely reviewers for a change with
gh arc cover - apply changes in a revision to the working copy with
gh arc patch - download a patch from Github with
gh arc export - update Git commit messages after review with
gh arc amend - push changes with
gh arc land - view enhanced information about Git branches with
gh arc branch
Once you've configured lint and unit test integration, you can also:
- check your code for syntax and style errors with
gh arc lint - run unit tests that cover your changes with
gh arc unit
This extension also integrates with other tools:
- create and view github gists with
gh arc gist
It has some advanced features as well, you can:
- enable tab completion with
gh arc completion - ...or extend the extension and add new commands
Github CLI does not currently support shell completion for extensions (See cli/cli#5309), but even if it did, who wants to type gh arc <TAB> <TAB> <TAB> ...that's just too much work if you ask me 🤷
So we've come up with a "simple as a rock" (is that a thing? 🤔) solution to both of the above problems 💡
-
Find out where github cli installs extensions on your machine. On Linux (and maybe on Mac), it's usually under
~/.local/share/gh/extensions. -
Create a simple
arcshell script file with the following contents:#!/usr/bin/env bash # Simple wrapper to enable completions for 'gh-arc' extension gh arc "$@"
-
Make it executable and place it on your
PATH. -
Get the completion script for your shell from the releases page. Don't forget to source it or whatever, you know what to do.
-
Then try typing
arc <TAB>...you should see the command completion for the extension; and as an added bonus, you've shortened the overallgh ...command as well... 🤯 Who said ingenious can't be simple? -
Profit... ⏱️
gh-arc can be configured using either JSON or YAML configuration files. The extension looks for configuration files in the following locations (in order of precedence):
- Current directory:
./.arc.json,./.arc.yaml, or./.arc.yml - User config:
~/.config/gh-arc/.arc.json,~/.config/gh-arc/.arc.yaml, or~/.config/gh-arc/.arc.yml - System-wide:
/etc/gh-arc/.arc.json,/etc/gh-arc/.arc.yaml, or/etc/gh-arc/.arc.yml
Within each directory, JSON files take precedence over YAML files (.arc.json > .arc.yaml > .arc.yml).
Create a .arc.json or .arc.yaml file in your project root or user config directory:
JSON format (.arc.json):
{
"github": {
"defaultBranch": "main",
"defaultReviewers": [],
"autoAssignReviewer": false
},
"diff": {
"createAsDraft": true,
"autoUpdatePR": true,
"includeCommitMessages": true,
"enableStacking": true,
"defaultBase": "",
"showStackingWarnings": true,
"templatePath": "",
"requireTestPlan": true,
"linearEnabled": false,
"linearDefaultProject": ""
},
"land": {
"defaultMergeMethod": "squash",
"deleteLocalBranch": true,
"deleteRemoteBranch": true,
"requireApproval": true,
"requireCI": true
},
"output": {
"verbose": false,
"quiet": false,
"json": false,
"color": true
}
}YAML format (.arc.yaml or .arc.yml):
github:
defaultBranch: main
defaultReviewers: []
autoAssignReviewer: false
diff:
createAsDraft: true
autoUpdatePR: true
includeCommitMessages: true
enableStacking: true
defaultBase: ""
showStackingWarnings: true
templatePath: ""
requireTestPlan: true
linearEnabled: false
linearDefaultProject: ""
land:
defaultMergeMethod: squash
deleteLocalBranch: true
deleteRemoteBranch: true
requireApproval: true
requireCI: true
output:
verbose: false
quiet: false
json: false
color: truegithub.defaultBranch(string, default:"main"): The default base branch for new PRsgithub.defaultReviewer(string, default:""): Default reviewer to assign to PRsgithub.autoAssignReviewer(bool, default:false): Automatically assign the default reviewer to new PRs
diff.createAsDraft(bool, default:true): Create PRs as drafts by defaultdiff.autoUpdatePR(bool, default:true): Automatically update existing PRs when runninggh arc diffdiff.includeCommitMessages(bool, default:true): Include commit messages in PR descriptiondiff.enableStacking(bool, default:true): Enable automatic stacked PR detectiondiff.defaultBase(string, default:""): Force a specific base branch (empty = auto-detect)diff.showStackingWarnings(bool, default:true): Show warnings when dependent PRs existdiff.templatePath(string, default:""): Path to custom PR template (empty = use built-in)diff.requireTestPlan(bool, default:true): Require test plan in PR templatediff.linearEnabled(bool, default:false): Enable Linear issue integrationdiff.linearDefaultProject(string, default:""): Default Linear project for issue references
land.defaultMergeMethod(string, default:"squash"): Default merge method (squash,merge, orrebase)land.deleteLocalBranch(bool, default:true): Delete local branch after landingland.deleteRemoteBranch(bool, default:true): Delete remote branch after landingland.requireApproval(bool, default:true): Require PR approval before landingland.requireCI(bool, default:true): Require CI checks to pass before landing
output.verbose(bool, default:false): Enable verbose outputoutput.quiet(bool, default:false): Suppress non-essential outputoutput.json(bool, default:false): Output results in JSON formatoutput.color(bool, default:true): Enable colored output
All configuration options can be overridden using environment variables with the GHARC_ prefix. Use underscores to separate nested keys:
# Override diff.createAsDraft
export GHARC_DIFF_CREATEASDRAFT=false
# Override github.defaultBranch
export GHARC_GITHUB_DEFAULTBRANCH=master
# Override land.defaultMergeMethod
export GHARC_LAND_DEFAULTMERGEMETHOD=mergeConfigure custom test runners in your .arc.json:
{
"test": {
"runners": [
{
"name": "go-test",
"command": "go",
"args": ["test", "./..."],
"workingDir": "",
"timeout": "5m"
}
]
}
}Configure custom linters:
{
"lint": {
"runners": [
{
"name": "golangci-lint",
"command": "golangci-lint",
"args": ["run"],
"workingDir": "",
"autoFix": true
}
],
"megaLinter": {
"enabled": "auto",
"config": ".mega-linter.yml",
"fixIssues": false
}
}
}For most projects, you only need to override a few settings:
JSON (.arc.json):
{
"diff": {
"createAsDraft": false,
"requireTestPlan": false
}
}YAML (.arc.yaml):
diff:
createAsDraft: false
requireTestPlan: falseThis configuration:
- Creates PRs as ready-for-review by default
- Makes the test plan field optional
All other settings will use their default values.