# Pull request bot

> What the LevelFour bot posts on GitHub pull requests and GitLab merge requests that change Terraform or CloudFormation, and the commands you can reply to it with.

Source: https://docs.levelfour.ai/integrations/pull-request-bot


Once [GitHub](/integrations/github) or [GitLab](/integrations/gitlab) is connected, the LevelFour bot reads every pull request or merge request that changes Terraform or CloudFormation. It posts the cost impact of the change back on the request, and it proposes cheaper settings where it finds them. It behaves the same on both providers.

## What it reads

| Language           | What the bot prices                                                                                                                                                     |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Terraform**      | The resources in your `.tf` files                                                                                                                                       |
| **CloudFormation** | YAML and JSON templates, SAM templates, nested stacks whose `TemplateURL` or `Location` names a file in the repository, and `AWS::Include` snippets from the repository |

A request that changes both gets one cost comment covering both. [How it prices a CloudFormation template](#how-it-prices-a-cloudformation-template) covers parameters, regions and what the bot cannot read.

## What it posts

| Item                   | Where                                                     | What it shows                                                                                                                                     |
| ---------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Cost comment**       | The conversation                                          | The cost of the changed resources before and after, and the monthly spend. The bot edits this one comment on each push instead of adding new ones |
| **LevelFour status**   | The checks panel on GitHub, the pipeline status on GitLab | That the analysis ran, and its summary                                                                                                            |
| **Inline suggestions** | On lines in the changed hunks of the diff                 | A concrete change to a resource, ready to apply                                                                                                   |

The **LevelFour** status reports the analysis, not a budget. On GitHub it passes, or reads neutral when the bot has suggestions for you. On GitLab it passes. It fails only when the analysis itself could not run, so it is not a gate on cost.

The bot posts a suggestion inline on any line of the diff it can comment on, context lines inside a changed hunk included. In a CloudFormation template the value must be written in the resource's own properties: a value that comes from a Parameter default or a Mapping gets no inline suggestion.

A change that touches no Terraform or CloudFormation resources gets no cost comment, and the status says why.

## How it prices a CloudFormation template

The bot resolves parameters, conditions, mappings and intrinsic functions before it prices a template. It prices a nested stack inside the template that creates it, with the parameters that template passes down.

Parameter values and the region come from `.levelfour/config.yml`, then from `samconfig`, then from the template's own defaults, with `us-east-1` as the last resort for the region. [PR bot configuration](/guides/pr-bot-configuration#cloudformation) has the order and the parameters file formats.

When the estimate rests on something the bot had to assume, the cost comment lists it under **Assumptions**:

* a parameter priced at its default
* a condition the bot could not decide, and assumed to hold
* a value CloudFormation reads at deploy time: `Fn::ImportValue`, a parameter stored in Systems Manager, a dynamic reference such as `{{resolve:ssm:...}}`

Read the list before you trust the total. If a stack deploys with values other than its defaults, map its parameters file under `cloudformation.parameter_files`.

> **Note**
>
> The bot does not price:
>
> * **AWS CDK apps.** Pricing one needs `cdk synth`, which the bot does not run. Run it yourself and point [`l4 estimate`](/cli/commands/estimate#cloudformation) at the synthesized templates.
> * **Serverless Framework** projects (`serverless.yml`).
> * **Registry modules**, the `::MODULE` resource types.

## Reply with a command

Mention the bot in a comment on the pull request or merge request, followed by a command. The bot reacts to your comment when it picks it up, then replies or updates the cost comment.

Anyone who can comment can run `help`. The commands that change something (`dismiss`, `snooze`, `ignore` and `apply`) need write access to the repository on GitHub, or the Developer role or above on GitLab. The bot answers anyone else with a note saying so.

| Command                             | What it does                                                          |
| ----------------------------------- | --------------------------------------------------------------------- |
| `@levelfourai help`                 | Posts the list of commands                                            |
| `@levelfourai dismiss <reason>`     | Dismisses the bot's suggestions on this request. A reason is required |
| `@levelfourai snooze <reason>`      | Hides the suggestions until the next push. A reason is required       |
| `@levelfourai ignore <type>.<name>` | Leaves one resource out of future analyses                            |
| `@levelfourai apply all`            | Commits every inline suggestion to the request's branch               |
| `@levelfourai apply <type>.<name>`  | Commits only the suggestions for that resource                        |

`<type>.<name>` names the resource in its own language. For Terraform it is the resource address from your `.tf` files. For CloudFormation it is the resource type, a dot, and the logical ID:

```text
@levelfourai ignore aws_ebs_volume.logs
@levelfourai ignore AWS::EC2::Volume.Logs
```

Two stacks can declare the same logical ID. Add `@` and the stack's scope to name the resource in one stack only. A root stack's scope is its template path. A nested stack's scope is the root template path followed by `/` and the logical ID of each nested stack resource on the way down, never the nested template's own path:

```text
@levelfourai ignore AWS::EC2::Volume.Logs@stacks/app.yaml
@levelfourai ignore AWS::RDS::DBInstance.Primary@stacks/app.yaml/Database
@levelfourai ignore AWS::RDS::DBInstance.Replica@stacks/app.yaml/Database/ReadReplicas
```

The second line names `Primary` in the stack that `stacks/app.yaml` creates through its `Database` resource. The third goes one level deeper, through `ReadReplicas` inside that stack.

Dismissing, snoozing and ignoring add a line to the top of the cost comment naming who did it, and the reason for a dismiss or snooze, so the next reviewer sees the decision.

### Applying suggestions

`apply` writes the suggestions into one commit on the branch and replies with its short SHA. On GitLab it also resolves the suggestion threads it applied. It does not run on a pull request or merge request from a fork, because the fork's branch belongs to its author.

On GitHub, if the branch moved after the bot's last analysis, the bot refuses rather than overwrite newer work. Push, let the cost comment update, and run `apply` again. `apply` also needs the LevelFour app to hold write access to repository contents. If it does not, the bot says so, and an organization owner approves the permission in GitHub.

## Tune it per repository

A `.levelfour/config.yml` at the root of the default branch turns the bot off for a repository, drops the inline suggestions, caps how many it posts, skips paths and resource types, or tells the bot which parameters file a template deploys with. [PR bot configuration](/guides/pr-bot-configuration) has every field.

## Next

* [PR bot configuration](/guides/pr-bot-configuration) is the `.levelfour/config.yml` reference
* [GitHub](/integrations/github) and [GitLab](/integrations/gitlab) cover connecting each provider
* [`l4 estimate`](/cli/commands/estimate) prices the same templates from a terminal or a CI job
