# ci.annotate

The `ci.annotate` function attaches an error, warning, or notice to a file and line, so it appears inline on the
pull request diff. Locally, it prints the same information as a compiler-style line on stderr.

## Usage

```python
ci.annotate(level, message, file = "", line = 0, end_line = 0, title = "")
```

## Arguments

- **`level`**
  Required. The severity, as a string: 
  `"error"`
  , 
  `"warning"`
  , or 
  `"notice"`
  . Any other value fails with an argument error.
- **`message`**
  Required. The text of the annotation, as a string.
- **`file`**

  (Optional) The path of the file the annotation belongs to, relative to the repository root. `line` and `end_line`
  need it: passing either without `file` fails with an argument error.
- **`line`**
  (Optional) The first line the annotation covers, as an integer. The default of 
  `0`
   means no line.
- **`end_line`**

  (Optional) The last line the annotation covers, as an integer. Use it to highlight a range. It needs `line`, and
  it cannot be smaller than `line`.
- **`title`**
  (Optional) A short heading shown above the message.

## Returns

The return value is `None`.

## Behavior

- In CI, the annotation appears on the changed line in the pull request diff and in the run's annotation list.
- Locally, the call prints `path:line: level: message` on stderr, and appends the `title` in parentheses when one is
  given. Without a line it prints `path: level: message`, and without a file it prints `level: message`.
- In CI the call is gated by `ci.annotations.enabled`. When it is off, the call warns, renders locally, and the
  script continues. See [gating](/functions/automation/ci#gating) and the [CI configuration](/cli/configuration/ci).

## Examples

### Flag a line in a file

```python
ci.annotate("warning", "This image tag is not pinned", file = "stacks/prod.yaml", line = 14, title = "Unpinned image")
```

Locally this prints `stacks/prod.yaml:14: warning: This image tag is not pinned (Unpinned image)`.

### Report each finding from a check

```python
findings = json.decode(exec.run(["tflint", "--format=json"], check = False, output = "capture").stdout)
for issue in findings["issues"]:
    ci.annotate(
        "error",
        issue["message"],
        file = issue["range"]["filename"],
        line = issue["range"]["start"]["line"],
        end_line = issue["range"]["end"]["line"],
        title = issue["rule"]["name"],
    )
```

## Errors

- An unknown `level`, or a missing `message`, fails with an argument error that names `ci.annotate`.
- A `line` or `end_line` without `file`, an `end_line` without `line`, or an `end_line` smaller than `line` fails with
  an argument error that names `ci.annotate`.
- A gated-off call never fails.

## Related

- [`ci`](/functions/automation/ci) describes the module and the shared behavior rules.
- [`ci.summary`](/functions/automation/ci.summary) and [`ci.comment`](/functions/automation/ci.comment) report results that are not tied to a line.
