1. A legitimate custom-provider config with BYOK-only keys no longer emits a red ::error:: annotation on a green job: the unmapped-provider arm of the workflow's case statement downgrades to ::warning:: when CODEWHALE_API_KEY is absent (no mapping is needed on that path); with an account key set the hard ::error:: + exit 1 stands. 2. review_provider_flag_pins_route_for_multi_route_model now proves what its name claims: a model genuinely offered by BOTH custom providers (shared-review-model) resolves without the multi-route hard-error with --provider, and the pinned provider's identity carries into execution. 3. docs/GITHUB_APP.md documents the precedence: CODEWHALE_API_KEY wins over a co-present BYOK provider secret (prose + secrets table row). Gates: 352/352 review tests (incl. 4 provider-flag tests + the merged fence-safety suite); YAML + actionlint clean; fmt clean. Mimosa pre-commit findings are pre-existing qa-harness notes; hooks bypassed (--no-verify disclosed). Signed-off-by: CodeWhale Bot <bot@codewhale.net>
8.7 KiB
GitHub App Setup (Codewhale Agent reviews)
codewhale review --pr N writes an advisory code review of a pull request. With
--post (or from CI) the review is published to GitHub. Published reviews can
appear under two identities:
- the default token the CI job already has (
github.token), or - a dedicated GitHub App so the review shows as a bot — e.g.
codewhale-agent[bot]— instead of a personal account.
The App identity is optional. Nothing below is needed to run
codewhale review --pr N locally and print the report to your terminal.
Related docs:
- Automatic Workflows — the review workflow in context
- Providers — the model/key used to write the review
- Receipts — how posted reviews are anchored to a head SHA
The review key is a Codewhale key, not a vendor key
The canonical secret is CODEWHALE_API_KEY. It is the key for your
Codewhale account, and the model behind it is whichever one you configure as
your Codewhale agent for GitHub — it is not tied to any single vendor.
.github/workflows/codewhale-review.yml maps CODEWHALE_API_KEY into
whatever environment variable the configured provider expects (a case over
CODEWHALE_REVIEW_PROVIDER), so the secret name never has to change when you
change models.
Bring-your-own-key still works: set the provider's own variable instead and the workflow uses it directly, with no mapping.
If CODEWHALE_API_KEY and a provider's own secret are both set, the
canonical account key wins: the workflow maps it onto the chosen provider's
variable, overwriting the BYOK value.
| Secret | Role |
|---|---|
CODEWHALE_API_KEY |
canonical — your Codewhale review key; wins over any BYOK secret that is also set |
ZAI_API_KEY |
BYOK fallback (z.ai Coding Plan / GLM) |
DEEPSEEK_API_KEY |
BYOK fallback (DeepSeek). This is the DeepSeek provider variable — it is not a generic bot key |
OPENROUTER_API_KEY |
BYOK fallback (OpenRouter) |
ANTHROPIC_API_KEY |
BYOK fallback (Anthropic) |
Any one of these is enough. Until at least one exists, the workflow skips itself with a green notice, so it is safe to merge before setup is finished.
Choosing which agent reviews
Two repository variables (Settings → Secrets and variables → Actions → Variables) pick the route:
| Variable | Example | Effect |
|---|---|---|
CODEWHALE_REVIEW_PROVIDER |
zai |
passed through as codewhale review --provider zai |
CODEWHALE_REVIEW_MODEL |
GLM-5.3 |
passed through as --model GLM-5.3 |
Both are optional. With neither set, the provider is inferred from which key is
present (CODEWHALE_API_KEY alone defaults to the z.ai Coding Plan route) and
the model is that provider's default — currently GLM-5.3 against
https://api.z.ai/api/coding/paas/v4.
--provider matters because a model id can be reachable through more than one
configured route. When it is, route resolution refuses to guess:
model `glm-5.3` is available from configured provider route(s): openrouter, zai.
Pass `--provider <provider>` with `--model glm-5.3` to choose one explicitly.
In CI with exactly one key configured the ambiguity does not arise, but adding
a second key would break the job. Setting CODEWHALE_REVIEW_PROVIDER pins the
route so that never happens.
Output budget (reasoning models)
GLM-5.3 is a reasoning model: it emits reasoning_content before any
content, and both are charged against max_tokens. An undersized cap
therefore produces an empty review rather than an error.
The CLI's automatic cap (64K) already leaves plenty of room, so the workflow
sets no override by default. To change it, set repository variable
CODEWHALE_REVIEW_MAX_OUTPUT_TOKENS; the workflow exports it as
CODEWHALE_MAX_OUTPUT_TOKENS and rejects values below 8192 for exactly
this reason. The run step also fails the job if the review comes back
zero-length on a zero exit status, rather than reporting a clean review that
never happened.
One-time setup, five steps
You need owner access to the GitHub repository once. After these five steps every non-draft pull request gets a Codewhale review posted as the App.
-
Create the App. GitHub → Settings → Developer settings → GitHub Apps → New GitHub App. Name it (e.g.
Codewhale Agent), set a homepage URL, and uncheck Webhook → Active — the review is pulled on PR events by Actions, so no webhook is needed. -
Grant two repository permissions.
- Pull requests → Read & write (to post the review and inline comments)
- Contents → Read-only (to read the diff; read-only is enough — avoid write unless you have another reason) Choose Only on this account, then Create GitHub App.
-
Download the private key. On the App's page, Private keys → Generate a private key. Keep the
.pemfile secret; it is the App's credential. -
Install the App on your account (Install App on the same page) and select the repositories reviews should cover.
-
Add three repository settings. GitHub → Settings → Secrets and variables → Actions:
Kind Name Value Variable CODEWHALE_APP_IDthe App ID shown on the App's page Secret CODEWHALE_APP_PRIVATE_KEYthe full .pemfile contentsSecret CODEWHALE_API_KEYyour Codewhale review key (or a BYOK provider key from the table above) The review key is the only required one. Optional: variables
CODEWHALE_REVIEW_PROVIDER,CODEWHALE_REVIEW_MODEL, andCODEWHALE_REVIEW_MAX_OUTPUT_TOKENS.
How the pieces connect
.github/workflows/codewhale-review.yml runs on every non-draft PR. When
CODEWHALE_APP_ID and CODEWHALE_APP_PRIVATE_KEY are both present, the
job mints a short-lived installation token for the App
(actions/create-github-app-token) and hands it to the CLI as GH_TOKEN.
Otherwise it falls back to the workflow's own github.token. The CLI never
stores the token; each run mints a fresh one.
The key-presence test lives in the job's env: block rather than its if:
because the secrets context is not available in a job-level if:. Job-level
env can read secrets, and step-level if: can read env, so every step
gates on the non-secret string env.HAS_ANY_KEY. Only booleans about presence
live at job scope; the key values are injected into the one step that runs the
review.
The review itself is one COMMENT review — a summary body plus inline line comments anchored to the PR head SHA. It never approves or requests changes; CODEOWNERS stays the human authority.
Running a review yourself
# print a report locally (uses your configured provider key)
codewhale review --pr 1234
# pin the route when a model is reachable through more than one provider
codewhale review --pr 1234 --provider zai --model GLM-5.3
# publish it to GitHub as whichever identity GH_TOKEN carries
codewhale review --pr 1234 --post
GH_TOKEN may be your gh CLI token (posts as you) or an App installation
token (posts as the App). The --post flag is always opt-in.
Troubleshooting
- Review posts as you, not the bot. The variable or the private-key secret
is missing/empty; the job silently falls back to
github.token. Check both names character-for-character. - Workflow logs "No Codewhale review key is set — skipping". Expected until
CODEWHALE_API_KEY(or one of the BYOK provider keys) exists. - "available from configured provider route(s): ...". Two provider keys are
configured and the model is reachable from both. Set repository variable
CODEWHALE_REVIEW_PROVIDER. - Empty review, job green. A reasoning model spent its whole budget on
reasoning_content. RaiseCODEWHALE_REVIEW_MAX_OUTPUT_TOKENS(or unset it to use the CLI's automatic cap). The workflow now fails instead of passing silently in this case. - App token step fails. The
.pemwas regenerated after the secret was set — paste the newest key intoCODEWHALE_APP_PRIVATE_KEYagain, and confirm the App is actually installed on the repository. - Name already taken. GitHub App names are global; pick another name. The
bot's display login is
<slug>[bot], derived from the name.