Project Adoption Guide#
pw_ghish: GitHub CLI-like interface for Gerrit code reviews and CI checks
While pw_ghish provides the ./gh CLI for Pigweed contributors, its
underlying engine (gh-ish) can be used with any project hosted on Gerrit
and LUCI.
This guide explains how to integrate gh-ish into a repository, build and
distribute the binary, configure project profiles, set up authentication, and
inspect CI builds.
Building and distributing gh-ish#
gh-ish is written in standard Go without external C library dependencies.
It can be built and distributed using Bazel, Go toolchains, or pre-built binary
packages.
Building with Bazel#
If your project uses Bazel, compile the binary target directly:
$ bazelisk build //pw_ghish:gh-ish
The compiled executable is placed in bazel-bin/pw_ghish/gh-ish_/gh-ish.
Building with standard Go#
You can compile and install gh-ish using the Go toolchain:
# Compile binary from source:
$ go build -o gh-ish ./pw_ghish/cmd/main.go
# Or install directly into $GOPATH/bin:
$ go install pigweed.dev/pw_ghish/cmd@latest
Distributing via CIPD or package managers#
For large multi-repo projects (such as Fuchsia or Chromium), gh-ish can be
packaged as a CIPD (Chrome Infrastructure Package Deployer) package or added to
host toolchains so developers and CI bots have it pre-installed on their
$PATH.
Creating a repository wrapper#
Rather than requiring every developer and AI coding agent to manually install
and update a global binary, we strongly recommend placing a lightweight wrapper
script named gh at the root of your repository (e.g. ./gh).
Why use a repository wrapper?#
Zero-setup onboarding: New contributors and AI agents can immediately run
./gh pr listor./gh pr statuswithout prior installation steps.Commit-pinned consistency: The wrapper automatically builds or downloads the exact version of the tool tied to the repository’s current commit, eliminating “works on my machine” version skew.
Transparent caching: The wrapper can build the binary once per commit and cache it in a local output directory (e.g.
out/gh/), providing instant sub-second execution on subsequent runs.Agent muscle memory: AI coding agents frequently check for a
./ghexecutable or default to GitHub CLI commands. Providing./ghallows agents to interact with the repository without custom instructions.
Example wrapper implementation#
Here is an example wrapper script that builds and caches the binary:
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CACHE_DIR="${REPO_ROOT}/out/gh"
COMMIT_HASH="$(git -C "${REPO_ROOT}" rev-parse HEAD 2>/dev/null || echo "latest")"
CACHED_BIN="${CACHE_DIR}/gh-ish-${COMMIT_HASH}"
if [[ ! -x "${CACHED_BIN}" ]]; then
mkdir -p "${CACHE_DIR}"
# Build using Bazel or Go:
bazelisk build --noshow_progress //pw_ghish:gh-ish
cp -f "${REPO_ROOT}/bazel-bin/pw_ghish/gh-ish_/gh-ish" "${CACHED_BIN}"
chmod +x "${CACHED_BIN}"
fi
exec "${CACHED_BIN}" "$@"
Project Profile Architecture#
Every Gerrit project has unique review conventions: differing label names
(such as Code-Review vs. Commit-Queue vs. Auto-Submit), varying
presubmit gates, and dedicated LUCI build buckets.
pw_ghish abstracts these differences through Project Profiles.
Built-in profiles#
pw_ghish includes built-in profiles for common Google open-source projects:
Profile |
Gerrit Host Match |
Auto-Submit |
Commit-Queue |
Code-Review |
Try Bucket |
|---|---|---|---|---|---|
pigweed |
|
|
|
|
|
fuchsia |
|
(triggers CQ) |
|
|
|
generic |
(fallback) |
(none) |
(none) |
|
(custom) |
Automatic profile detection#
pw_ghish automatically selects the appropriate profile by inspecting:
The Git remote URL (e.g.
origin) for the repository.The Gerrit review host hostname.
The Git config setting
ghish.profile.
Explicit profile override#
You can force a specific profile on any command using the --profile flag:
$ gh-ish pr list --profile fuchsia
$ gh-ish pr status --profile pigweed
Profile names are strictly validated. Passing an invalid profile name halts with an error listing all available profiles.
Adding a new project profile#
To add a profile for your project, implement the ProjectProfile interface in
pw_ghish/profile.go (or embed genericProfile for default behavior) and
register it with RegisterProfile:
type myProjectProfile struct {
genericProfile
}
func (p *myProjectProfile) Name() string {
return "myproject"
}
func (p *myProjectProfile) DefaultGerritHost() string {
return "https://myproject-review.googlesource.com/a"
}
func (p *myProjectProfile) CQLabel() (LabelVote, bool) {
return LabelVote{Name: "Commit-Queue", Value: 2}, true
}
func (p *myProjectProfile) BuildbucketProject() string {
return "myproject"
}
func init() {
RegisterProfile(&myProjectProfile{})
}
Add a test case in pw_ghish/profile_test.go verifying detection and behavior.
Authentication and Credential Setup#
pw_ghish supports multiple authentication strategies, from standard open-source
cookie and token files to automated CI bot credentials and Google-internal
developer workstations.
Credential discovery order#
When communicating with Gerrit REST APIs, pw_ghish searches for credentials in
the following order:
Explicit Environment Token: Reads bearer or personal access tokens from the
GERRIT_TOKENenvironment variable. Recommended for automated CI pipelines and bots.$ export GERRIT_TOKEN="your-http-access-token"
Git Cookies (
.gitcookies): Parses Netscape-formatted cookie jars at~/.gitcookiesor the path specified bygit config http.cookiefile. This is the standard mechanism used by Git-on-Borg and Google Open Source Gerrit hosts.To generate cookies for a Google-hosted Gerrit server: * Visit the Gerrit web UI (e.g.
https://your-host-review.googlesource.com). * Click your user avatar and select Settings -> HTTP Credentials. * Click Generate Password and copy the provided command into your terminal.Netrc (
.netrc): Parses machine entries in~/.netrcmatching the target Gerrit hostname.Internal Workstation Transport: When running on Google-internal developer workstations,
pw_ghishautomatically integrates with local authentication helpers without requiring manual configuration.Anonymous Read Fallback: If no credentials are found, public read operations (such as
pr view,pr list,pr diff, andpr checks) fall back to unauthenticated access on public Gerrit hosts. Write operations (such aspr push,pr comment, orpr merge) report an actionable authentication error.
Forcing authentication method#
In automated environments or debugging sessions, you can strictly enforce a
specific authentication backend using the GH_ISH_AUTH_METHOD environment
variable:
$ export GH_ISH_AUTH_METHOD=cookies # Strictly require .gitcookies
$ export GH_ISH_AUTH_METHOD=token # Strictly require GERRIT_TOKEN
$ export GH_ISH_AUTH_METHOD=none # Force anonymous access
$ export GH_ISH_AUTH_METHOD=gob-curl # Force workstation helper transport
If the requested authentication method cannot be satisfied (e.g. missing cookie
file or missing token), pw_ghish immediately halts with a descriptive error
rather than silently falling back to anonymous access.
LUCI CI and Buildbucket Integration#
Projects using LUCI (such as Chromium, Fuchsia, Pigweed, and Android) benefit from direct Buildbucket and LogDog integration:
pRPC checks querying#
pw_ghish pr checks sends lightweight pRPC queries directly to
cr-buildbucket.appspot.com to fetch builder statuses, run times, and direct
Milo build URLs. It operates without web scraping or headless browser overhead.
Terminal log inspection#
pw_ghish pr checks log queries LogDog streams to retrieve the tail of failed
build steps, providing immediate terminal diagnostics for broken tests and lints
without opening a web browser.
Builder reruns#
pw_ghish pr checks rerun automatically constructs and executes bb add
commands targeting your profile’s try bucket:
# Rerun all failed builders on the current change:
$ gh-ish pr checks rerun --failed
# Rerun a specific builder:
$ gh-ish pr checks rerun myproject-linux-dbg
# Preview the generated bb command without executing:
$ gh-ish pr checks rerun --failed --dry-run