http response bitbucket /plugins/servlet/applinks/whoami 200 pint
http response bitbucket /rest/api/1.0/projects/prometheus/repos/rules/commits/.*/pull-requests 200 {"size":1,"isLastPage":true,"values":[{"id":1,"open":true,"fromRef":{"id":"refs/heads/v2","latestCommit":"fake-commit-id"},"toRef":{"id":"refs/heads/main","latestCommit":"fake-commit-id"}}]}
http response bitbucket /rest/api/latest/projects/prometheus/repos/rules/pull-requests/1/activities 200 {"size":0,"isLastPage":true,"values":[]}
http response bitbucket /rest/api/1.0/projects/prometheus/repos/rules/pull-requests/1/comments 200 {}
http start bitbucket 127.0.0.1:6076

mkdir testrepo
cd testrepo
exec git init --initial-branch=main .

cp ../src/v1.yml rules.yml
cp ../src/.pint.hcl .
env GIT_AUTHOR_NAME=pint
env GIT_AUTHOR_EMAIL=pint@example.com
env GIT_COMMITTER_NAME=pint
env GIT_COMMITTER_EMAIL=pint@example.com
exec git add .
exec git commit -am 'import rules and config'

exec git checkout -b v2
cp ../src/v2.yml rules.yml
exec git commit -am 'v2'

env BITBUCKET_AUTH_TOKEN="12345"
! exec pint -l warn --no-color ci --require-owner
! stdout .
cmp stderr ../stderr.txt
cmp bitbucket.got ../bitbucket.expected

-- src/v1.yml --
groups:
- name: mygroup
  rules:
  - record: rule1
    expr: sum(foo) by(job)
-- src/v2.yml --
groups:
- name: mygroup
  rules:
  - alert: syntax error
    expr: sum(foo) bar

  - alert: missing required fields
    expr: no_such_metric{job="fake"}

  - record: vector_matching
    expr: up{job="prometheus"} / prometheus_build_info{job="prometheus"}

  - alert: count
    expr: up{job="prometheus"} == 0
    for: 2m
    labels:
      notify: blackhole

  - alert: for_and_rate
    expr: rate(no_such_metric[10s])
    for: 0m
    labels:
      notify: blackhole

  - alert: template
    expr: sum(no_such_metric) by(foo) > 0
    labels:
      value: '{{ $value }}'
    annotations:
      instance: 'sum on {{ $labels.instance }} is {{ $value }}'

  - alert: fragile
    expr: errors / sum(requests) without(rack)

  - record: regexp
    expr: sum(no_such_metric{job=~"fake"})

  - alert: dups
    expr: errors / sum(requests) without(rack)
    #expr: errors / sum(requests) without(rack)
    #alert: dups
-- src/.pint.hcl --
ci {
  baseBranch = "main"
}
parser {
  include    = [".+.yml"]
}
repository {
  bitbucket {
    uri        = "http://127.0.0.1:6076"
    timeout    = "10s"
    project    = "prometheus"
    repository = "rules"
  }
}
rule {
  match {
    kind = "recording"
  }
  aggregate ".+" {
    severity = "bug"
    keep     = ["job"]
  }
}
rule {
  match {
    kind = "alerting"
  }
  annotation "link" {
    severity = "bug"
    value    = "http://runbooks.example.com/(.+)"
    required = true
  }
}
rule {
  match {
    kind = "alerting"
  }
  ignore {
    kind = "alerting"
    label "notify" {
      value = "blackhole"
    }
  }
  annotation "summary" {
    severity = "bug"
    required = true
  }
  annotation "dashboard" {
    severity = "bug"
    value    = "https://grafana.example.com/(.+)"
  }
  label "priority" {
    severity = "bug"
    value    = "(1|2|3|4|5)"
    required = true
  }
  label "notify" {
    severity = "bug"
    required = true
  }
  label "component" {
    severity = "bug"
    required = true
  }
}

-- stderr.txt --
Bug: required annotation not set (alerts/annotation)
  ---> rules.yml:4-5 -> `syntax error` [+6 duplicates]
5 |     expr: sum(foo) bar
              ^^^ `link` annotation is required.

Bug: required annotation not set (alerts/annotation)
  ---> rules.yml:4-5 -> `syntax error` [+4 duplicates]
5 |     expr: sum(foo) bar
              ^^^ `summary` annotation is required.

Bug: required label not set (rule/label)
  ---> rules.yml:4-5 -> `syntax error` [+4 duplicates]
5 |     expr: sum(foo) bar
              ^^^ `component` label is required.

Bug: required label not set (rule/label)
  ---> rules.yml:4-5 -> `syntax error` [+4 duplicates]
5 |     expr: sum(foo) bar
              ^^^ `notify` label is required.

Bug: required label not set (rule/label)
  ---> rules.yml:4-5 -> `syntax error` [+4 duplicates]
5 |     expr: sum(foo) bar
              ^^^ `priority` label is required.

Bug: missing owner (rule/owner)
  ---> rules.yml:4-5 -> `syntax error` [+8 duplicates]
5 |     expr: sum(foo) bar
              ^^^
              `rule/owner` comments are required in all files, please add a `# pint file/owner $owner`
              somewhere in this file and/or `# pint rule/owner $owner` on top of each rule.

Fatal: PromQL syntax error (promql/syntax)
  ---> rules.yml:5 -> `syntax error`
5 |     expr: sum(foo) bar
                       ^^^ unexpected identifier "bar"

Warning: always firing alert (alerts/comparison)
  ---> rules.yml:8 -> `missing required fields` [+3 duplicates]
8 |     expr: no_such_metric{job="fake"}
              ^^^^^^^^^^^^^^^^^^^^^^^^^^
              This query doesn't have any condition and so this alert will always fire if it matches
              anything.

Information: redundant field with default value (alerts/for)
  ---> rules.yml:21 -> `for_and_rate`
21 |     for: 0m
              ^^ `0m` is the default value of `for`, this line is unnecessary.

Bug: template uses non-existent label (alerts/template)
  ---> rules.yml:25-30 -> `template`
26 |     expr: sum(no_such_metric) by(foo) > 0
                                   ^^
                                   Query is using aggregation with `by(foo)`, only labels included inside
                                   `by(...)` will be present on the results.
   |     [...]
30 |       instance: 'sum on {{ $labels.instance }} is {{ $value }}'
                                       ^^^^^^^^^
                                       Template is using `instance` label but the query results won't
                                       have this label.

Bug: value used in labels (alerts/template)
  ---> rules.yml:28 -> `template`
28 |       value: '{{ $value }}'
                   ^^^^^^^^^^^^
                   Using `$value` in labels will generate a new alert on every value change, move it to
                   annotations.

Warning: redundant regexp (promql/regexp)
  ---> rules.yml:36 -> `regexp`
36 |     expr: sum(no_such_metric{job=~"fake"})
                                  ^^^^^^^^^^^
                                  Unnecessary regexp match on static string `job=~"fake"`, use
                                  `job="fake"` instead.

Bug: required label is being removed via aggregation (promql/aggregate)
  ---> rules.yml:36 -> `regexp`
36 |     expr: sum(no_such_metric{job=~"fake"})
               ^^^ Query is using aggregation that removes all labels.
                   `job` label is required and should be preserved when aggregating all rules.

level=ERROR msg="Execution completed with error(s)" err="problems found"
-- bitbucket.expected --
GET /rest/api/1.0/projects/prometheus/repos/rules/commits/.*/pull-requests
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json

GET /plugins/servlet/applinks/whoami
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json

GET /rest/api/latest/projects/prometheus/repos/rules/pull-requests/1/activities
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json

POST /rest/api/1.0/projects/prometheus/repos/rules/pull-requests/1/comments
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json
--- BODY ---
text: |
  :stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **alerts/annotation** check.

  <details>
  <summary>required annotation not set</summary>

  ```yaml
  5 |     expr: sum(foo) bar
                ^^^
  ```

  `link` annotation is required.

  </details>

  ------

  The same issue was reported 6 more time(s), duplicates where suppressed.

  <details>
  <summary>Show affected rules</summary>

  - `missing required fields` at `rules.yml:7`
  - `count` at `rules.yml:13`
  - `for_and_rate` at `rules.yml:19`
  - `template` at `rules.yml:25`
  - `fragile` at `rules.yml:32`
  - `dups` at `rules.yml:38`

  </details>

  ------

  :information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/alerts/annotation.html).
severity: NORMAL
anchor:
  path: rules.yml
  lineType: ADDED
  fileType: TO
  diffType: EFFECTIVE
  line: 5
--- END ---

POST /rest/api/1.0/projects/prometheus/repos/rules/pull-requests/1/comments
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json
--- BODY ---
text: |
  :stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **rule/label** check.

  <details>
  <summary>required label not set</summary>

  ```yaml
  5 |     expr: sum(foo) bar
                ^^^
  ```

  `component` label is required.

  </details>

  ------

  The same issue was reported 4 more time(s), duplicates where suppressed.

  <details>
  <summary>Show affected rules</summary>

  - `missing required fields` at `rules.yml:7`
  - `template` at `rules.yml:25`
  - `fragile` at `rules.yml:32`
  - `dups` at `rules.yml:38`

  </details>

  ------

  :information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/rule/label.html).
severity: NORMAL
anchor:
  path: rules.yml
  lineType: ADDED
  fileType: TO
  diffType: EFFECTIVE
  line: 5
--- END ---

POST /rest/api/1.0/projects/prometheus/repos/rules/pull-requests/1/comments
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json
--- BODY ---
text: |
  :stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **rule/owner** check.

  <details>
  <summary>missing owner</summary>

  ```yaml
  5 |     expr: sum(foo) bar
                ^^^
  ```

  `rule/owner` comments are required in all files, please add a `# pint file/owner $owner` somewhere in this file and/or `# pint rule/owner $owner` on top of each rule.

  </details>

  ------

  The same issue was reported 8 more time(s), duplicates where suppressed.

  <details>
  <summary>Show affected rules</summary>

  - `missing required fields` at `rules.yml:7`
  - `vector_matching` at `rules.yml:10`
  - `count` at `rules.yml:13`
  - `for_and_rate` at `rules.yml:19`
  - `template` at `rules.yml:25`
  - `fragile` at `rules.yml:32`
  - `regexp` at `rules.yml:35`
  - `dups` at `rules.yml:38`

  </details>

  ------

  :information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/rule/owner.html).
severity: NORMAL
anchor:
  path: rules.yml
  lineType: ADDED
  fileType: TO
  diffType: EFFECTIVE
  line: 5
--- END ---

POST /rest/api/1.0/projects/prometheus/repos/rules/pull-requests/1/comments
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json
--- BODY ---
text: |
  :stop_sign: **Fatal** reported by [pint](https://cloudflare.github.io/pint/) **promql/syntax** check.

  <details>
  <summary>PromQL syntax error</summary>

  ```yaml
  5 |     expr: sum(foo) bar
                         ^^^
  ```

  unexpected identifier "bar"

  [Click here](https://prometheus.io/docs/prometheus/latest/querying/basics/) for PromQL documentation.

  </details>

  ------

  :information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/promql/syntax.html).
severity: NORMAL
anchor:
  path: rules.yml
  lineType: ADDED
  fileType: TO
  diffType: EFFECTIVE
  line: 5
--- END ---

POST /rest/api/1.0/projects/prometheus/repos/rules/pull-requests/1/comments
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json
--- BODY ---
text: |
  :warning: **Warning** reported by [pint](https://cloudflare.github.io/pint/) **alerts/comparison** check.

  <details>
  <summary>always firing alert</summary>

  ```yaml
  8 |     expr: no_such_metric{job="fake"}
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
  ```

  This query doesn't have any condition and so this alert will always fire if it matches anything.

  Prometheus alerting rules will trigger an alert for each query that returns *any* result.
  Unless you do want an alert to always fire you should write your query in a way that returns results only when some condition is met.
  In most cases this can be achieved by having some condition in the query expression.
  For example `up == 0` or `rate(error_total[2m]) > 0`.
  Be careful as some PromQL operations will cause the query to always return the results, for example using the [bool modifier](https://prometheus.io/docs/prometheus/latest/querying/operators/#comparison-binary-operators).

  </details>

  ------

  The same issue was reported 3 more time(s), duplicates where suppressed.

  <details>
  <summary>Show affected rules</summary>

  - `for_and_rate` at `rules.yml:20`
  - `fragile` at `rules.yml:33`
  - `dups` at `rules.yml:39`

  </details>

  ------

  :information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/alerts/comparison.html).
severity: NORMAL
anchor:
  path: rules.yml
  lineType: ADDED
  fileType: TO
  diffType: EFFECTIVE
  line: 8
--- END ---

POST /rest/api/1.0/projects/prometheus/repos/rules/pull-requests/1/comments
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json
--- BODY ---
text: |
  :information_source: **Information** reported by [pint](https://cloudflare.github.io/pint/) **alerts/for** check.

  <details>
  <summary>redundant field with default value</summary>

  ```yaml
  21 |     for: 0m
                ^^
  ```

  `0m` is the default value of `for`, this line is unnecessary.

  </details>

  ------

  :information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/alerts/for.html).
severity: NORMAL
anchor:
  path: rules.yml
  lineType: ADDED
  fileType: TO
  diffType: EFFECTIVE
  line: 21
--- END ---

POST /rest/api/1.0/projects/prometheus/repos/rules/pull-requests/1/comments
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json
--- BODY ---
text: |
  :stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **alerts/template** check.

  <details>
  <summary>template uses non-existent label</summary>

  ```yaml
  26 |     expr: sum(no_such_metric) by(foo) > 0
                                     ^^
  ```

  Query is using aggregation with `by(foo)`, only labels included inside `by(...)` will be present on the results.

  ```yaml
  30 |       instance: 'sum on {{ $labels.instance }} is {{ $value }}'
                                         ^^^^^^^^^
  ```

  Template is using `instance` label but the query results won't have this label.

  </details>

  ------

  :information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/alerts/template.html).
severity: NORMAL
anchor:
  path: rules.yml
  lineType: ADDED
  fileType: TO
  diffType: EFFECTIVE
  line: 30
--- END ---

POST /rest/api/1.0/projects/prometheus/repos/rules/pull-requests/1/comments
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json
--- BODY ---
text: |
  :stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **alerts/template** check.

  <details>
  <summary>value used in labels</summary>

  ```yaml
  28 |       value: '{{ $value }}'
                     ^^^^^^^^^^^^
  ```

  Using `$value` in labels will generate a new alert on every value change, move it to annotations.

  </details>

  ------

  :information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/alerts/template.html).
severity: NORMAL
anchor:
  path: rules.yml
  lineType: ADDED
  fileType: TO
  diffType: EFFECTIVE
  line: 28
--- END ---

POST /rest/api/1.0/projects/prometheus/repos/rules/pull-requests/1/comments
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json
--- BODY ---
text: |
  :warning: **Warning** reported by [pint](https://cloudflare.github.io/pint/) **promql/regexp** check.

  <details>
  <summary>redundant regexp</summary>

  ```yaml
  36 |     expr: sum(no_such_metric{job=~"fake"})
                                    ^^^^^^^^^^^
  ```

  Unnecessary regexp match on static string `job=~"fake"`, use `job="fake"` instead.

  See [Prometheus documentation](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors) for details on how vector selectors work.

  </details>

  ------

  :information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/promql/regexp.html).
severity: NORMAL
anchor:
  path: rules.yml
  lineType: ADDED
  fileType: TO
  diffType: EFFECTIVE
  line: 36
--- END ---

POST /rest/api/1.0/projects/prometheus/repos/rules/pull-requests/1/comments
  Accept-Encoding: gzip
  Authorization: Bearer "12345"
  Content-Type: application/json
--- BODY ---
text: |
  :stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **promql/aggregate** check.

  <details>
  <summary>required label is being removed via aggregation</summary>

  ```yaml
  36 |     expr: sum(no_such_metric{job=~"fake"})
                 ^^^
  ```

  Query is using aggregation that removes all labels.

  `job` label is required and should be preserved when aggregating all rules.

  </details>

  ------

  :information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/promql/aggregate.html).
severity: NORMAL
anchor:
  path: rules.yml
  lineType: ADDED
  fileType: TO
  diffType: EFFECTIVE
  line: 36
--- END ---

