GitHub App and Lambda (webhook trigger)¶
You can trigger Neptune from webhooks: when a PR is opened or updated, neptune plan runs; when someone comments e.g. @neptbot apply on a PR, neptune apply runs. The default is to install the Neptune project's neptbot GitHub App on your repos—no need to create an app or run infrastructure. Alternatively, you can self-host by creating your own GitHub App and deploying the Lambda in this repo.
You can also run Neptune from a workflow that triggers on pull_request and/or workflow_dispatch (see Installation).
Default: Install neptbot¶
- Install the neptbot GitHub App on your organization or user account: Install neptbot.
- Add the workflow below to each repository where you want Neptune to run. The app will trigger it via
repository_dispatchwhen a PR is opened/updated or when someone comments@neptbot applyor@neptbot plan. - Ensure PRs have the label
neptune— neptbot triggersrepository_dispatchonly for pull requests that have the label neptune. To apply the label automatically, use the labeler GitHub Action: add a workflow (e.g..github/workflows/labeler.yml) that runs the labeler onpull_request, and a config file (e.g..github/labeler.yml) that assigns theneptunelabel based on changed files (e.g. when PRs touch your Terraform/OpenTofu paths). See the terramate-stacks or automerge examples for reference. - Configure object storage (e.g. S3) and a
.neptune.yamlin the repo as required by Neptune (see Configuration and Object storage). - Optionally add branch protection so that neptune apply is a required status check (see Branch protection (recommended)).
No Lambda or AWS setup is required; the Neptune project runs the webhook endpoint.
Why we request these permissions¶
When you install neptbot, GitHub will show the permissions the app requests. Here is why each one is needed:
| Permission | Level | Why we need it |
|---|---|---|
| Contents | Read and write | Required by GitHub to trigger your repository’s workflow via the Repository dispatch API. Neptbot does not read or modify your repository contents; it only sends the dispatch event so your GitHub Actions workflow runs. |
| Pull requests | Read and write | Read: We receive webhook events when a PR is opened, updated, or labeled so we can trigger plan. We also need to read PR context when you comment @neptbot apply or @neptbot plan. Write: We add a 👀 (eyes) reaction on the PR after we trigger the workflow so you can see that neptbot received the event. |
| Issues | Read and write | Pull requests are treated as issues in the GitHub API. Write is required to add a 👀 reaction to the PR and to the comment that triggered apply or plan, so you get a visible confirmation that the command was received. We do not create, edit, or close issues or PRs. |
| Metadata | Read | Default repository metadata (e.g. name, visibility). Required for GitHub Apps that need to identify the repository. |
Neptbot only triggers your workflow and adds reactions; it does not push code, merge PRs, or access your source beyond what is needed for those actions.
Workflow on repository_dispatch¶
Each repository that has the Neptune GitHub App installed (neptbot or your own) must have a workflow that listens for the dispatch event. Example:
name: neptune
on:
repository_dispatch:
types: [neptune-command]
concurrency:
group: neptune-${{ github.event.client_payload.pull_request_number }}
cancel-in-progress: false
permissions:
contents: read
pull-requests: write
statuses: write
jobs:
neptune:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: refs/pull/${{ github.event.client_payload.pull_request_number }}/head
- name: Install Neptune
run: |
NEPTUNE_VERSION="v0.2.0"
curl -sSL "https://github.com/devopsfactory-io/neptune/releases/download/${NEPTUNE_VERSION}/neptune_${NEPTUNE_VERSION}_linux_amd64.tar.gz" | tar -xz -C /usr/local/bin neptune
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.5.0"
- name: Run Neptune
run: neptune command ${{ github.event.client_payload.command }}
env:
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_PULL_REQUEST_NUMBER: ${{ github.event.client_payload.pull_request_number }}
GITHUB_PULL_REQUEST_BRANCH: ${{ github.event.client_payload.pull_request_branch }}
GITHUB_RUN_ID: ${{ github.run_id }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Object storage and .neptune.yaml must be configured (see docs/object-storage.md and docs/configuration.md)
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ vars.AWS_REGION }}
Notes:
- concurrency: For Terraform/Neptune workflows, use
cancel-in-progress: falseso in-progress runs are never cancelled when a new event arrives. - checkout: Uses
refs/pull/<number>/headso the job runs on the PR branch; the payload'spull_request_branchandpull_request_shaare available if you need them. - Neptune env:
GITHUB_REPOSITORY,GITHUB_PULL_REQUEST_NUMBER,GITHUB_PULL_REQUEST_BRANCH,GITHUB_RUN_ID, andGITHUB_TOKENare required by Neptune.
4. Comment format¶
To run apply or plan from a PR comment, write a comment that:
- Mentions the app. When using neptbot, use
@neptbot. If you self-host with a different app slug, use that slug (e.g.@your-app-slug). - Contains the word apply or plan.
Examples:
@neptbot apply@neptbot planPlease run @neptbot apply when ready
Only comments on pull requests (not on issues) are handled.
5. Branch protection (recommended)¶
To block merging until apply has run successfully, add neptune apply as a required status check in your branch protection rules. The workflow above uses statuses: write so Neptune can set the neptune plan and neptune apply commit statuses.
Alternative: Self-hosted (your own GitHub App and Lambda)¶
If you want to run your own GitHub App and Lambda (e.g. in your AWS account), use the Lambda code and CloudFormation in this repo. Your repositories will use the same repository_dispatch workflow and payload as above; the only difference is who hosts the app and webhook endpoint.
1. Create your own GitHub App¶
- Create a GitHub App (e.g. under your user or org).
- Webhook: Leave "Active" checked; set Payload URL to your Lambda Function URL (you get this after deploying the stack; see lambda/README.md).
- Webhook secret: Generate a secret and store it in AWS Secrets Manager (see below).
- Permissions: Repository → Contents (Read and write), Pull requests (Read and write), Issues (Read and write), Metadata (Read). The
repository_dispatchAPI requires write access. Issues (Read and write) is required for the Lambda to add a 👀 reaction to the PR and to the comment; Pull requests (Read and write) is also recommended for reactions on pull requests—see GitHub App permissions. - Subscribe to events: Pull requests, Issue comments.
- Private key: Generate and download the PEM; store it in AWS Secrets Manager.
- Install the App on the repositories where you want Neptune to run.
2. Deploy the Lambda¶
- Build the Lambda binary and zip it (see lambda/README.md).
- Create two secrets in AWS Secrets Manager: one with the webhook secret (plain string), one with the private key (full PEM).
- Upload the zip to S3 and deploy the CloudFormation stack from lambda/cloudformation/template.yaml (see lambda/README.md).
- Copy the stack output WebhookUrl into your GitHub App's Payload URL.
3. Add the workflow to your repositories¶
Add the same workflow shown in Workflow on repository_dispatch above to each repo that has your App installed.
4. Optional: Require a PR label¶
To run Neptune only on infrastructure-related PRs, set the NEPTUNE_PR_LABEL parameter when deploying the CloudFormation stack (e.g. NeptunePrLabel=neptune). The Lambda will then trigger repository_dispatch and add the eyes reaction only when the PR has that label. Add the label (e.g. neptune) to PRs that should run Neptune; leave it unset or use a different label for other PRs. If you omit this parameter (or leave it empty), all matching PRs trigger the workflow as before.
Payload sent by the Lambda¶
The webhook handler (neptbot or your Lambda) triggers repository_dispatch with:
- event_type:
neptune-command - client_payload:
command:"plan"or"apply"pull_request_number: PR numberpull_request_branch: head ref (e.g.feature/my-branch)pull_request_sha: head commit SHA (optional but useful)pull_request_repo_full:owner/repo(optional)
Your workflow reads these from github.event.client_payload and passes the required env to the neptune CLI.
Contents: we only call the Repository dispatch API to trigger your workflow. We don't read or change your code. PRs/Issues: we read events and add a reaction so you see we got the command. Metadata: repo id. We never push, merge, or edit issues.