exec bash -x ./webserver.sh &
exec bash -c 'I=0 ; while [ ! -f server.pid ] && [ $I -lt 30 ]; do sleep 1; I=$((I+1)); done'

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"
pint.error -l warn --no-color ci --require-owner
! stdout .
cmp stderr ../stderr.txt
exec sh -c 'cat ../server.pid | xargs kill'

-- 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"
}
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
  }
}

-- webserver.go --
package main

import (
	"context"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, "OK")
	})

	listener, err := net.Listen("tcp", "127.0.0.1:6076")
	if err != nil {
		log.Fatal(err)
	}

	server := &http.Server{
		Addr: "127.0.0.1:6076",
	}

	go func() {
		_ = server.Serve(listener)
	}()

	pid := os.Getpid()
	err = os.WriteFile("server.pid", []byte(strconv.Itoa(pid)), 0644)
	if err != nil {
		log.Fatal(err)
	}

	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		time.Sleep(time.Minute*2)
		stop <- syscall.SIGTERM
	}()
	<-stop
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	server.Shutdown(ctx)
}

-- webserver.sh --
env GOCACHE=$TMPDIR go run webserver.go

-- stderr.txt --
rules.yml:4-5: link annotation is required (alerts/annotation)
  - alert: syntax error
    expr: sum(foo) bar

rules.yml:4-5: summary annotation is required (alerts/annotation)
  - alert: syntax error
    expr: sum(foo) bar

rules.yml:4-5: component label is required (rule/label)
  - alert: syntax error
    expr: sum(foo) bar

rules.yml:4-5: notify label is required (rule/label)
  - alert: syntax error
    expr: sum(foo) bar

rules.yml:4-5: priority label is required (rule/label)
  - alert: syntax error
    expr: sum(foo) bar

rules.yml:4-5: 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 (rule/owner)
  - alert: syntax error
    expr: sum(foo) bar

rules.yml:5: syntax error: unexpected identifier "bar" (promql/syntax)
    expr: sum(foo) bar

rules.yml:7-8: link annotation is required (alerts/annotation)
  - alert: missing required fields
    expr: no_such_metric{job="fake"}

rules.yml:7-8: summary annotation is required (alerts/annotation)
  - alert: missing required fields
    expr: no_such_metric{job="fake"}

rules.yml:7-8: component label is required (rule/label)
  - alert: missing required fields
    expr: no_such_metric{job="fake"}

rules.yml:7-8: notify label is required (rule/label)
  - alert: missing required fields
    expr: no_such_metric{job="fake"}

rules.yml:7-8: priority label is required (rule/label)
  - alert: missing required fields
    expr: no_such_metric{job="fake"}

rules.yml:7-8: 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 (rule/owner)
  - alert: missing required fields
    expr: no_such_metric{job="fake"}

rules.yml:8: alert query doesn't have any condition, it will always fire if the metric exists (alerts/comparison)
    expr: no_such_metric{job="fake"}

rules.yml:10-11: 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 (rule/owner)
  - record: vector_matching
    expr: up{job="prometheus"} / prometheus_build_info{job="prometheus"}

rules.yml:13-17: link annotation is required (alerts/annotation)
  - alert: count
    expr: up{job="prometheus"} == 0
    for: 2m
    labels:
      notify: blackhole

rules.yml:13-17: 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 (rule/owner)
  - alert: count
    expr: up{job="prometheus"} == 0
    for: 2m
    labels:
      notify: blackhole

rules.yml:19-23: link annotation is required (alerts/annotation)
  - alert: for_and_rate
    expr: rate(no_such_metric[10s])
    for: 0m
    labels:
      notify: blackhole

rules.yml:19-23: 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 (rule/owner)
  - alert: for_and_rate
    expr: rate(no_such_metric[10s])
    for: 0m
    labels:
      notify: blackhole

rules.yml:20: alert query doesn't have any condition, it will always fire if the metric exists (alerts/comparison)
    expr: rate(no_such_metric[10s])

rules.yml:21: "0m" is the default value of "for", consider removing this line (alerts/for)
    for: 0m

rules.yml:25-30: 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 (rule/owner)
  - alert: template
    expr: sum(no_such_metric) by(foo) > 0
    labels:
      value: '{{ $value }}'
    annotations:
      instance: 'sum on {{ $labels.instance }} is {{ $value }}'

rules.yml:26-30: template is using "instance" label but the query removes it (alerts/template)
    expr: sum(no_such_metric) by(foo) > 0
    labels:
      value: '{{ $value }}'
    annotations:
      instance: 'sum on {{ $labels.instance }} is {{ $value }}'

rules.yml:27-28: component label is required (rule/label)
    labels:
      value: '{{ $value }}'

rules.yml:27-28: notify label is required (rule/label)
    labels:
      value: '{{ $value }}'

rules.yml:27-28: priority label is required (rule/label)
    labels:
      value: '{{ $value }}'

rules.yml:28: using $value in labels will generate a new alert on every value change, move it to annotations (alerts/template)
      value: '{{ $value }}'

rules.yml:29-30: link annotation is required (alerts/annotation)
    annotations:
      instance: 'sum on {{ $labels.instance }} is {{ $value }}'

rules.yml:29-30: summary annotation is required (alerts/annotation)
    annotations:
      instance: 'sum on {{ $labels.instance }} is {{ $value }}'

rules.yml:32-33: link annotation is required (alerts/annotation)
  - alert: fragile
    expr: errors / sum(requests) without(rack)

rules.yml:32-33: summary annotation is required (alerts/annotation)
  - alert: fragile
    expr: errors / sum(requests) without(rack)

rules.yml:32-33: component label is required (rule/label)
  - alert: fragile
    expr: errors / sum(requests) without(rack)

rules.yml:32-33: notify label is required (rule/label)
  - alert: fragile
    expr: errors / sum(requests) without(rack)

rules.yml:32-33: priority label is required (rule/label)
  - alert: fragile
    expr: errors / sum(requests) without(rack)

rules.yml:32-33: 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 (rule/owner)
  - alert: fragile
    expr: errors / sum(requests) without(rack)

rules.yml:33: alert query doesn't have any condition, it will always fire if the metric exists (alerts/comparison)
    expr: errors / sum(requests) without(rack)

rules.yml:33: aggregation using without() can be fragile when used inside binary expression because both sides must have identical sets of labels to produce any results, adding or removing labels to metrics used here can easily break the query, consider aggregating using by() to ensure consistent labels (promql/fragile)
    expr: errors / sum(requests) without(rack)

rules.yml:35-36: 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 (rule/owner)
  - record: regexp
    expr: sum(no_such_metric{job=~"fake"})

rules.yml:36: job label is required and should be preserved when aggregating "^.+$" rules, use by(job, ...) (promql/aggregate)
    expr: sum(no_such_metric{job=~"fake"})

rules.yml:36: unnecessary regexp match on static string job=~"fake", use job="fake" instead (promql/regexp)
    expr: sum(no_such_metric{job=~"fake"})

rules.yml:38-39: link annotation is required (alerts/annotation)
  - alert: dups
    expr: errors / sum(requests) without(rack)

rules.yml:38-39: summary annotation is required (alerts/annotation)
  - alert: dups
    expr: errors / sum(requests) without(rack)

rules.yml:38-39: component label is required (rule/label)
  - alert: dups
    expr: errors / sum(requests) without(rack)

rules.yml:38-39: notify label is required (rule/label)
  - alert: dups
    expr: errors / sum(requests) without(rack)

rules.yml:38-39: priority label is required (rule/label)
  - alert: dups
    expr: errors / sum(requests) without(rack)

rules.yml:38-39: 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 (rule/owner)
  - alert: dups
    expr: errors / sum(requests) without(rack)

rules.yml:39: alert query doesn't have any condition, it will always fire if the metric exists (alerts/comparison)
    expr: errors / sum(requests) without(rack)

rules.yml:39: aggregation using without() can be fragile when used inside binary expression because both sides must have identical sets of labels to produce any results, adding or removing labels to metrics used here can easily break the query, consider aggregating using by() to ensure consistent labels (promql/fragile)
    expr: errors / sum(requests) without(rack)

level=fatal msg="Fatal error" error="submitting reports: fatal error(s) reported"
