Post reviews as a GitHub App¶
This is optional branding on top of the Marketplace action — not a second install
lgtmaybe itself is installed from the GitHub Marketplace listing as an Action in your workflow — see Use as a GitHub Action. The GitHub App on this page is one you create yourself so reviews post under your bot identity; there is no shared lgtmaybe App to install, because lgtmaybe has no hosted backend — a shared App would need its private key distributed, which is exactly what this design avoids.
By default lgtmaybe posts reviews as github-actions[bot] using the workflow's
built-in token. The recommended setup is to point it at a GitHub App you
own so reviews post under that App's identity instead — a branded
lgtmaybe[bot] name and avatar, a higher API rate limit, and optional access
across several repositories. The default token still works and every workflow
falls back to it when the App inputs are empty, but the App identity is worth
the one-time setup: reviews are clearly attributed, and busy repos don't share
the workflow token's rate limit.
This changes only the posting identity. The action still runs in your own CI, still fetches the diff via the API without checking out PR code, and the keyless cloud auth (Bedrock/Vertex/Azure) is untouched. There is no hosted service — you are not sending your code or keys anywhere new.
Contents¶
- How it works
- One-time GitHub App setup
- Workflow example
- Reviewing across several repositories
- Without the built-in inputs
- Troubleshooting
How it works¶
You pass the App's ID and private key as action inputs. The action mints a
short-lived installation token with
actions/create-github-app-token,
uses it to read the PR and post the review, and revokes it in a post step at the
end of the job. Nothing long-lived is stored, and the token's scope is only what
the App is installed for.
One-time GitHub App setup¶
This is the human-only part — do it once:
- Create a GitHub App (Settings → Developer settings → GitHub Apps → New, at
the personal or organisation level). Name it — the name becomes the
[bot]identity on the review, e.g.lgtmaybe. - Under Repository permissions, grant:
- Pull requests: Read and write — to post the review and inline comments.
- Contents: Read-only — to read the config and files.
- (Optional) Issues: Read and write if you use
/ask, which replies on the PR conversation. - You do not need to subscribe to any webhook events — the App is used only to mint a token; the review is still triggered by your workflow.
- Install the App on the repositories you want reviewed (the App's page → Install App).
- Note the App ID (on the App's General page) and generate a private key
(same page → Private keys → Generate). The key downloads as a
.pemfile. - Store them in the repo (or org):
- App ID as a repository variable, e.g.
LGTMAYBE_APP_ID(Settings → Secrets and variables → Actions → Variables). - Private key as a secret, e.g.
LGTMAYBE_APP_PRIVATE_KEY(paste the whole.pemcontents).
Workflow example¶
Add app_id and app_private_key to any provider workflow. Everything else is
unchanged — this example is the anthropic workflow with the App identity added:
name: lgtmaybe
on:
pull_request_target:
issue_comment:
types: [created]
permissions:
contents: read # still needed for the actions/checkout of .lgtmaybe.yml
pull-requests: write
jobs:
review:
# Only trusted authors can trigger a review (see "Who can trigger a review").
if: >-
(github.event_name == 'pull_request_target' &&
contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association)) ||
(github.event.issue.pull_request &&
contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: MattJColes/lgtmaybe@v1
with:
provider: anthropic
model: claude-sonnet-4-6
api_key: ${{ secrets.ANTHROPIC_API_KEY }}
app_id: ${{ vars.LGTMAYBE_APP_ID }}
app_private_key: ${{ secrets.LGTMAYBE_APP_PRIVATE_KEY }}
The App's installation permissions govern what the review can post; the
workflow permissions: block still applies to the default token that
actions/checkout uses to read .lgtmaybe.yml.
Reviewing across several repositories¶
By default the minted token is scoped to the current repository. To let one App
review PRs across a monorepo or several repos in an org, set app_owner (and
optionally app_repositories):
- uses: MattJColes/lgtmaybe@v1
with:
provider: anthropic
model: claude-sonnet-4-6
api_key: ${{ secrets.ANTHROPIC_API_KEY }}
app_id: ${{ vars.LGTMAYBE_APP_ID }}
app_private_key: ${{ secrets.LGTMAYBE_APP_PRIVATE_KEY }}
app_owner: my-org
app_repositories: |
service-a
service-b
With app_owner set and app_repositories empty, the token covers every repo
the App is installed on under that owner.
Without the built-in inputs¶
The app_id / app_private_key inputs are a convenience — because github_token
is already a pass-through input, you can mint the token yourself and pass it in.
This is useful if you already run actions/create-github-app-token for other
steps:
- uses: actions/create-github-app-token@v2
id: app-token
with:
app-id: ${{ vars.LGTMAYBE_APP_ID }}
private-key: ${{ secrets.LGTMAYBE_APP_PRIVATE_KEY }}
- uses: MattJColes/lgtmaybe@v1
with:
provider: anthropic
model: claude-sonnet-4-6
api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ steps.app-token.outputs.token }}
Troubleshooting¶
Reviews still post as github-actions[bot] — app_id is empty, so the action
fell back to the default token. Check that the LGTMAYBE_APP_ID variable and
LGTMAYBE_APP_PRIVATE_KEY secret are set and referenced correctly (a variable is
${{ vars.* }}, a secret is ${{ secrets.* }}).
RequestError [HttpError]: Not Found / Resource not accessible by
integration — the App is not installed on the repository, or lacks a required
permission. Install it on the repo and confirm Pull requests: write +
Contents: read under the App's Repository permissions (re-accept the
permission change on the installation if you edited it after installing).
private key ... failed to parse — the secret must contain the full PEM,
including the -----BEGIN...----- / -----END...----- lines. Paste the whole
.pem file contents.