Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package conftest for openSUSE:Factory 
checked in at 2026-10-06 19:17:43
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/conftest (Old)
 and      /work/SRC/openSUSE:Factory/.conftest.new.3698736 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "conftest"

Tue Oct  6 19:17:43 2026 rev:42 rq:1382662 version:0.71.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/conftest/conftest.changes        2026-09-21 
12:13:58.431101979 +0200
+++ /work/SRC/openSUSE:Factory/.conftest.new.3698736/conftest.changes   
2026-10-06 19:17:46.662928630 +0200
@@ -1,0 +2,25 @@
+Thu Oct 01 05:00:11 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.71.0:
+  * New Features
+    - 636cfe3: feat: Add parser for CODEOWNER files (#1404)
+      (@apnsngr)
+  * Bug Fixes
+    - 96fc461: fix(docs): unbreak Netlify build on the Noble image
+      (#1433) (@boranx)
+    - 8345c40: fix: emit a single TAP plan across all files (#1435)
+      (@behappymia)
+  * OPA Changes
+    - b254dd7: build(deps): bump github.com/open-policy-agent/opa
+      from 1.20.2 to 1.21.0 (#1434) (@dependabot[bot])
+  * Other Changes
+    - a766df6: build(deps): bump alpine from 3.24.1 to 3.24.2
+      (#1430) (@dependabot[bot])
+    - 6febea0: build(deps): bump github.com/magiconair/properties
+      (#1431) (@dependabot[bot])
+    - e0275ea: chore: run goreleaser in parallel with docker builds
+      (#1427) (@jalseth)
+    - f1484a6: refactor(outputter): Standardize how totals are
+      accumulated (#1429) (@jalseth)
+
+-------------------------------------------------------------------

Old:
----
  conftest-0.70.1.obscpio

New:
----
  conftest-0.71.0.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ conftest.spec ++++++
--- /var/tmp/diff_new_pack.KWT4EC/_old  2026-10-06 19:17:48.568008408 +0200
+++ /var/tmp/diff_new_pack.KWT4EC/_new  2026-10-06 19:17:48.570008492 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           conftest
-Version:        0.70.1
+Version:        0.71.0
 Release:        0
 Summary:        Tool to write tests against structured configuration data
 License:        Apache-2.0
@@ -26,7 +26,7 @@
 Source1:        vendor.tar.gz
 BuildRequires:  bash-completion
 BuildRequires:  fish
-BuildRequires:  go1.26 >= 1.26.3
+BuildRequires:  go1.27 >= 1.27.1
 BuildRequires:  zsh
 
 %description

++++++ _service ++++++
--- /var/tmp/diff_new_pack.KWT4EC/_old  2026-10-06 19:17:48.603009878 +0200
+++ /var/tmp/diff_new_pack.KWT4EC/_new  2026-10-06 19:17:48.607010046 +0200
@@ -4,7 +4,7 @@
     <param name="scm">git</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
-    <param name="revision">refs/tags/v0.70.1</param>
+    <param name="revision">refs/tags/v0.71.0</param>
     <param name="changesgenerate">enable</param>
   </service>
   <service name="set_version" mode="manual">

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.KWT4EC/_old  2026-10-06 19:17:48.628010927 +0200
+++ /var/tmp/diff_new_pack.KWT4EC/_new  2026-10-06 19:17:48.631011053 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/open-policy-agent/conftest.git</param>
-              <param 
name="changesrevision">0b42cb5cd1aa1dc1c9025a5e83c7e963166d08c0</param></service></servicedata>
+              <param 
name="changesrevision">8345c40e21f3e6ab1159f4e15585b5f7d6aa32a7</param></service></servicedata>
 (No newline at EOF)
 

++++++ conftest-0.70.1.obscpio -> conftest-0.71.0.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/.github/workflows/post_merge.yaml 
new/conftest-0.71.0/.github/workflows/post_merge.yaml
--- old/conftest-0.70.1/.github/workflows/post_merge.yaml       2026-09-19 
18:32:18.000000000 +0200
+++ new/conftest-0.71.0/.github/workflows/post_merge.yaml       2026-09-30 
03:15:00.000000000 +0200
@@ -20,7 +20,7 @@
         run: docker buildx create --name conftestbuild --use
 
       - name: Build Docker image
-        uses: 
docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # 
ratchet:docker/[email protected]
+        uses: 
docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # 
ratchet:docker/[email protected]
         with:
           context: '.'
           push: false
@@ -44,7 +44,7 @@
         run: docker buildx create --name conftestbuild --use
 
       - name: Build Docker image
-        uses: 
docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # 
ratchet:docker/[email protected]
+        uses: 
docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # 
ratchet:docker/[email protected]
         with:
           context: '.'
           target: examples
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/.github/workflows/release.yaml 
new/conftest-0.71.0/.github/workflows/release.yaml
--- old/conftest-0.70.1/.github/workflows/release.yaml  2026-09-19 
18:32:18.000000000 +0200
+++ new/conftest-0.71.0/.github/workflows/release.yaml  2026-09-30 
03:15:00.000000000 +0200
@@ -26,9 +26,8 @@
         with:
           persist-credentials: false
 
-      # It is important to check the GoReleaser config before pushing to
-      # Dockerhub to avoid having mismatches between what is in Dockerhub
-      # and GitHub releases.
+      # Validate the GoReleaser config before building anything so a bad
+      # config fails fast rather than after images have been pushed.
       - name: check releaser config
         uses: 
goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # 
ratchet:goreleaser/[email protected]
         with:
@@ -64,7 +63,7 @@
 
       - name: Build and push Docker image
         id: build
-        uses: 
docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # 
ratchet:docker/[email protected]
+        uses: 
docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # 
ratchet:docker/[email protected]
         with:
           context: .
           push: true
@@ -100,7 +99,7 @@
 
       - name: Build and push examples image
         id: build
-        uses: 
docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # 
ratchet:docker/[email protected]
+        uses: 
docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # 
ratchet:docker/[email protected]
         with:
           context: .
           push: true
@@ -143,8 +142,7 @@
     permissions:
       contents: write # GoReleaser creates the GitHub release.
     needs:
-      - docker-conftest
-      - docker-examples
+      - check
     steps:
       - name: checkout source
         uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # 
ratchet:actions/checkout@v5
@@ -158,6 +156,11 @@
           go-version-file: 'go.mod'
           cache: false
 
+      - name: install syft
+        uses: 
anchore/sbom-action/download-syft@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # 
ratchet:anchore/sbom-action/[email protected]
+        with:
+          syft-version: v1.52.0
+
       - name: release
         uses: 
goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # 
ratchet:goreleaser/[email protected]
         with:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/.goreleaser.yml 
new/conftest-0.71.0/.goreleaser.yml
--- old/conftest-0.70.1/.goreleaser.yml 2026-09-19 18:32:18.000000000 +0200
+++ new/conftest-0.71.0/.goreleaser.yml 2026-09-30 03:15:00.000000000 +0200
@@ -47,6 +47,9 @@
 checksum:
   name_template: 'checksums.txt'
 
+sboms:
+- artifacts: archive
+
 # The release is published by the workflow once all attestations succeed.
 release:
   draft: true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/Dockerfile 
new/conftest-0.71.0/Dockerfile
--- old/conftest-0.70.1/Dockerfile      2026-09-19 18:32:18.000000000 +0200
+++ new/conftest-0.71.0/Dockerfile      2026-09-30 03:15:00.000000000 +0200
@@ -51,7 +51,7 @@
 WORKDIR /examples
 
 ## RELEASE ##
-FROM alpine:3.24.1
+FROM alpine:3.24.2
 
 # Install git for protocols that depend on it when using conftest pull
 RUN apk add --no-cache git
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/acceptance.bats 
new/conftest-0.71.0/acceptance.bats
--- old/conftest-0.70.1/acceptance.bats 2026-09-19 18:32:18.000000000 +0200
+++ new/conftest-0.71.0/acceptance.bats 2026-09-30 03:15:00.000000000 +0200
@@ -286,6 +286,12 @@
   [ "$status" -eq 0 ]
 }
 
+@test "Can parse CODEOWNERS files" {
+  run ./conftest test -p examples/codeowners/policy 
examples/codeowners/CODEOWNERS
+  [ "$status" -eq 0 ]
+  [[ "$output" =~ "4 tests, 4 passed" ]]
+}
+
 @test "Can parse stdin with parser flag" {
   run bash -c "cat examples/ini/grafana.ini | ./conftest test -p 
examples/ini/policy --parser ini -"
   [ "$status" -eq 1 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/docs/examples.md 
new/conftest-0.71.0/docs/examples.md
--- old/conftest-0.70.1/docs/examples.md        2026-09-19 18:32:18.000000000 
+0200
+++ new/conftest-0.71.0/docs/examples.md        2026-09-30 03:15:00.000000000 
+0200
@@ -6,6 +6,7 @@
 - [AWS SAM 
Framework](https://github.com/open-policy-agent/conftest/tree/master/examples/awssam)
 - 
[Builtin-Errors](https://github.com/open-policy-agent/conftest/tree/master/examples/builtin-errors)
 - 
[Capabilities](https://github.com/open-policy-agent/conftest/tree/master/examples/capabilities)
+- 
[CODEOWNERS](https://github.com/open-policy-agent/conftest/tree/master/examples/codeowners)
 - 
[Combine](https://github.com/open-policy-agent/conftest/tree/master/examples/combine)
 - 
[Configfile](https://github.com/open-policy-agent/conftest/tree/master/examples/configfile)
 - [CUE](https://github.com/open-policy-agent/conftest/tree/master/examples/cue)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/docs/index.md 
new/conftest-0.71.0/docs/index.md
--- old/conftest-0.70.1/docs/index.md   2026-09-19 18:32:18.000000000 +0200
+++ new/conftest-0.71.0/docs/index.md   2026-09-30 03:15:00.000000000 +0200
@@ -84,6 +84,7 @@
 
 As of today Conftest supports:
 
+- CODEOWNERS
 - CUE
 - CycloneDX
 - Dockerfile
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/examples/codeowners/CODEOWNERS 
new/conftest-0.71.0/examples/codeowners/CODEOWNERS
--- old/conftest-0.70.1/examples/codeowners/CODEOWNERS  1970-01-01 
01:00:00.000000000 +0100
+++ new/conftest-0.71.0/examples/codeowners/CODEOWNERS  2026-09-30 
03:15:00.000000000 +0200
@@ -0,0 +1,18 @@
+# This is a comment.
+# Each line is a file pattern followed by one or more owners.
+
+# These owners will be the default owners for everything in
+# the repo.
+*       @bob @carol
+
+# Order is important; the last matching pattern takes the most
+# precedence. When someone opens a pull request that only
+# modifies JS files, only @my-org/js-devs and not the global
+# owner(s) will be requested for a review.
+*.js    @my-org/js-devs # This is an inline comment.
+
+*.go @my-org/go-devs
+
+docs/ @my-org/librarians
+
+very/important/file @bob
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/conftest-0.70.1/examples/codeowners/policy/policy.rego 
new/conftest-0.71.0/examples/codeowners/policy/policy.rego
--- old/conftest-0.70.1/examples/codeowners/policy/policy.rego  1970-01-01 
01:00:00.000000000 +0100
+++ new/conftest-0.71.0/examples/codeowners/policy/policy.rego  2026-09-30 
03:15:00.000000000 +0200
@@ -0,0 +1,33 @@
+package main
+import rego.v1
+
+# Check that file starts with default owner
+deny contains msg if {
+  input.rules[0].pattern != "*"
+  msg := "first rule must assign default owner"
+}
+
+deny contains msg if {
+  some rule in input.rules
+  rule.pattern == "*.js"
+  rule.owners != [{
+    "type": "team",
+    "value": "my-org/js-devs",
+  }]
+  msg := "JavaScript files must be owned by my-org/js-devs"
+}
+
+deny contains msg if {
+  some rule in input.rules
+  rule.pattern == "*.go"
+  rule.owners != [{
+    "type": "team",
+    "value": "my-org/go-devs",
+  }]
+  msg := "go files must be owned by my-org/go-devs"
+}
+
+deny contains msg if {
+  input.rules[-1].pattern != "very/important/file"
+  msg := "last rule must assign dedicated owner for very/important/file"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/go.mod new/conftest-0.71.0/go.mod
--- old/conftest-0.70.1/go.mod  2026-09-19 18:32:18.000000000 +0200
+++ new/conftest-0.71.0/go.mod  2026-09-30 03:15:00.000000000 +0200
@@ -18,13 +18,14 @@
        github.com/google/go-jsonnet v0.22.0
        github.com/hashicorp/go-getter v1.8.9
        github.com/hashicorp/hcl v1.0.0
+       github.com/hmarr/codeowners v1.2.1
        github.com/jstemmer/go-junit-report v1.0.0
        github.com/logrusorgru/aurora v2.0.3+incompatible
-       github.com/magiconair/properties v1.18.11
+       github.com/magiconair/properties v1.18.12
        github.com/moby/buildkit v0.33.0
        github.com/muhammadmuzzammil1998/jsonc v1.0.0
-       github.com/olekukonko/tablewriter v1.1.4
-       github.com/open-policy-agent/opa v1.20.2
+       github.com/olekukonko/tablewriter v1.1.5
+       github.com/open-policy-agent/opa v1.21.0
        github.com/opencontainers/image-spec v1.1.1
        github.com/owenrumney/go-sarif/v2 v2.3.3
        github.com/shteou/go-ignore v0.3.1
@@ -96,7 +97,7 @@
        github.com/go-logr/logr v1.4.4 // indirect
        github.com/go-logr/stdr v1.2.2 // indirect
        github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
-       github.com/gobwas/glob v0.2.3 // indirect
+       github.com/gobwas/glob v1.0.0 // indirect
        github.com/goccy/go-json v0.10.6 // indirect
        github.com/google/s2a-go v0.1.9 // indirect
        github.com/google/uuid v1.6.0 // indirect
@@ -107,13 +108,13 @@
        github.com/hashicorp/go-version v1.9.0 // indirect
        github.com/hashicorp/hcl/v2 v2.23.0 // indirect
        github.com/inconshreveable/mousetrap v1.1.0 // indirect
-       github.com/klauspost/compress v1.19.2 // indirect
+       github.com/klauspost/compress v1.20.0 // indirect
        github.com/lestrrat-go/blackmagic v1.0.4 // indirect
-       github.com/lestrrat-go/dsig v1.3.0 // indirect
+       github.com/lestrrat-go/dsig v1.4.0 // indirect
        github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect
        github.com/lestrrat-go/httpcc v1.0.1 // indirect
        github.com/lestrrat-go/httprc/v3 v3.0.6 // indirect
-       github.com/lestrrat-go/jwx/v3 v3.2.0 // indirect
+       github.com/lestrrat-go/jwx/v3 v3.3.0 // indirect
        github.com/lestrrat-go/option/v2 v2.0.0 // indirect
        github.com/mattn/go-colorable v0.1.14 // indirect
        github.com/mattn/go-isatty v0.0.20 // indirect
@@ -142,7 +143,7 @@
        github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 // 
indirect
        github.com/ulikunitz/xz v0.5.16 // indirect
        github.com/valyala/fastjson v1.6.10 // indirect
-       github.com/vektah/gqlparser/v2 v2.5.36 // indirect
+       github.com/vektah/gqlparser/v2 v2.5.37 // indirect
        github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // 
indirect
        github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 
// indirect
        github.com/yashtewari/glob-intersection v0.2.0 // indirect
@@ -159,13 +160,13 @@
        go.yaml.in/yaml/v2 v2.4.4 // indirect
        go.yaml.in/yaml/v3 v3.0.5 // indirect
        golang.org/x/crypto v0.56.0 // indirect
-       golang.org/x/mod v0.40.0 // indirect
+       golang.org/x/mod v0.41.0 // indirect
        golang.org/x/net v0.58.0 // indirect
        golang.org/x/oauth2 v0.36.0 // indirect
-       golang.org/x/sync v0.22.0 // indirect
-       golang.org/x/sys v0.47.0 // indirect
-       golang.org/x/text v0.41.0 // indirect
-       golang.org/x/time v0.15.0 // indirect
+       golang.org/x/sync v0.23.0 // indirect
+       golang.org/x/sys v0.48.0 // indirect
+       golang.org/x/text v0.42.0 // indirect
+       golang.org/x/time v0.16.0 // indirect
        golang.org/x/tools v0.49.0 // indirect
        google.golang.org/api v0.294.0 // indirect
        google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d // 
indirect
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/go.sum new/conftest-0.71.0/go.sum
--- old/conftest-0.70.1/go.sum  2026-09-19 18:32:18.000000000 +0200
+++ new/conftest-0.71.0/go.sum  2026-09-30 03:15:00.000000000 +0200
@@ -176,8 +176,9 @@
 github.com/go-test/deep v1.0.7/go.mod 
h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
 github.com/go-viper/mapstructure/v2 v2.5.0 
h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
 github.com/go-viper/mapstructure/v2 v2.5.0/go.mod 
h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
-github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
 github.com/gobwas/glob v0.2.3/go.mod 
h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
+github.com/gobwas/glob v1.0.0 h1:p+FKbLEIsK1yZ39/OINwFvqNb5oyPY4H8xcy6uYu8dg=
+github.com/gobwas/glob v1.0.0/go.mod 
h1:oWCdo522i2P1n/hMXGNWs7yoV4wy/ciZuUIbvKj5rkc=
 github.com/goccy/go-json v0.10.6 
h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
 github.com/goccy/go-json v0.10.6/go.mod 
h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
 github.com/godoctor/godoctor v0.0.0-20181123222458-69df17f3a6f6/go.mod 
h1:+tyhT8jBF8E0XvdlSXOSL7Iko7DlNiongHq3q+wcsPs=
@@ -214,6 +215,8 @@
 github.com/hashicorp/hcl v1.0.0/go.mod 
h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/hashicorp/hcl/v2 v2.23.0 
h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
 github.com/hashicorp/hcl/v2 v2.23.0/go.mod 
h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
+github.com/hmarr/codeowners v1.2.1 
h1:+9yndrwG0UVP1GkLBEQMSbSUNeLpbrbL924SRthA/9k=
+github.com/hmarr/codeowners v1.2.1/go.mod 
h1:KPlR1p/B4owPjwfNIBueWlOP4CmqlQFX9b6nANG6j40=
 github.com/imega/luaformatter v0.0.0-20211025140405-86b0a68d6bef 
h1:RC993DdTIHNItsyLj79fgZNLzrf9tBN0GR6W5ZPms6s=
 github.com/imega/luaformatter v0.0.0-20211025140405-86b0a68d6bef/go.mod 
h1:i2XCfvmO94HrEOQWllihhtPrkvNfuB2R2p/o6+OVnRU=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod 
h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@@ -222,8 +225,8 @@
 github.com/jstemmer/go-junit-report v1.0.0 
h1:8X1gzZpR+nVQLAht+L/foqOeX2l9DTZoaIPbEQHxsds=
 github.com/jstemmer/go-junit-report v1.0.0/go.mod 
h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
 github.com/k0kubun/pp v3.0.1+incompatible/go.mod 
h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
-github.com/klauspost/compress v1.19.2 
h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8=
-github.com/klauspost/compress v1.19.2/go.mod 
h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
+github.com/klauspost/compress v1.20.0 
h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
+github.com/klauspost/compress v1.20.0/go.mod 
h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
 github.com/kr/pretty v0.1.0/go.mod 
h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod 
h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -235,16 +238,16 @@
 github.com/kylelemons/godebug v1.1.0/go.mod 
h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/lestrrat-go/blackmagic v1.0.4 
h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
 github.com/lestrrat-go/blackmagic v1.0.4/go.mod 
h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
-github.com/lestrrat-go/dsig v1.3.0 
h1:phjMOCXvYzhuIgn7Voe2rex8z166vGfxRxmqM25P9/Q=
-github.com/lestrrat-go/dsig v1.3.0/go.mod 
h1:RD2eOaidyPvpc7IJQoO3Qq52RWdy8ZcJs8lrOnoa1Kc=
+github.com/lestrrat-go/dsig v1.4.0 
h1:g7LUjK8cT74A5DzBXJI5HzsJuLhoYN0Wzj4nuOMIrH8=
+github.com/lestrrat-go/dsig v1.4.0/go.mod 
h1:I8Nddg/vN2cUl/h8N7SRRApLnNNeyZPIqLYpvpOtGGo=
 github.com/lestrrat-go/dsig-secp256k1 v1.0.0 
h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY=
 github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod 
h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU=
 github.com/lestrrat-go/httpcc v1.0.1 
h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
 github.com/lestrrat-go/httpcc v1.0.1/go.mod 
h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
 github.com/lestrrat-go/httprc/v3 v3.0.6 
h1:4FpLQ18KK/ypPbVU3NLWJNRvH3kcYiqKqWfKGqNWxxI=
 github.com/lestrrat-go/httprc/v3 v3.0.6/go.mod 
h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0=
-github.com/lestrrat-go/jwx/v3 v3.2.0 
h1:Jb3zBASTSZXz7gzzSAfYqxXF8KejvKC4xWoePLQqXCA=
-github.com/lestrrat-go/jwx/v3 v3.2.0/go.mod 
h1:38vQ8iWKq3qRSbilbzvzdQPuywhowwuR03lhkYskyrw=
+github.com/lestrrat-go/jwx/v3 v3.3.0 
h1:OXcYvQOQ7cxWzeZ/Q9sYk8ABe/kCSI371WmuACiCT+4=
+github.com/lestrrat-go/jwx/v3 v3.3.0/go.mod 
h1:eIJhDcKHBwcgxqv8RiIylV67TVl1wJp/265IAHY1Db8=
 github.com/lestrrat-go/option/v2 v2.0.0 
h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss=
 github.com/lestrrat-go/option/v2 v2.0.0/go.mod 
h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg=
 github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
@@ -252,8 +255,8 @@
 github.com/logrusorgru/aurora v2.0.3+incompatible 
h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
 github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod 
h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
 github.com/magiconair/properties v1.8.0/go.mod 
h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.18.11 
h1:j5ozYZl0zCjG7ahMDH0GWIobOvvUzT0BdAguG0ViKy0=
-github.com/magiconair/properties v1.18.11/go.mod 
h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/magiconair/properties v1.18.12 
h1:sT9zQpvTB3B4gzrX0tmZNTEaGyg8Zw55MFYRE32Mr9I=
+github.com/magiconair/properties v1.18.12/go.mod 
h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
 github.com/mattn/go-colorable v0.1.4/go.mod 
h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-colorable v0.1.14 
h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
 github.com/mattn/go-colorable v0.1.14/go.mod 
h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
@@ -283,10 +286,10 @@
 github.com/olekukonko/errors v1.2.0/go.mod 
h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
 github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA=
 github.com/olekukonko/ll v0.1.6/go.mod 
h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88=
-github.com/olekukonko/tablewriter v1.1.4 
h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
-github.com/olekukonko/tablewriter v1.1.4/go.mod 
h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
-github.com/open-policy-agent/opa v1.20.2 
h1:dlX5VEGA5M62W8+X1nRPEaQIh/CMRn5FcD1mebpKWKg=
-github.com/open-policy-agent/opa v1.20.2/go.mod 
h1:yYKTQAlkbefoXSYChT4ek8gc8WCtScL7L7OSZxFVe0s=
+github.com/olekukonko/tablewriter v1.1.5 
h1:4LoZSfMySpMQY3PT8RWJsJeuEuMIoo9xGRgvmqjg6IQ=
+github.com/olekukonko/tablewriter v1.1.5/go.mod 
h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
+github.com/open-policy-agent/opa v1.21.0 
h1:k/N0fieTkBPM0H7mIOrMd/xZPaMsxW70jIzIPeOBst4=
+github.com/open-policy-agent/opa v1.21.0/go.mod 
h1:eJL6KUOIaW5YLnhJEA6sm3FOYRDJaHZvYT6geATbpPk=
 github.com/opencontainers/go-digest v1.0.0 
h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod 
h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.1.1 
h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
@@ -306,8 +309,8 @@
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_golang v1.24.1 
h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU=
 github.com/prometheus/client_golang v1.24.1/go.mod 
h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE=
-github.com/prometheus/client_model v0.6.2 
h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
-github.com/prometheus/client_model v0.6.2/go.mod 
h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/client_model v0.6.3 
h1:O0jaTVAYNxTHYInEPFJt5I3+sN8zqBtVMPTB1qyxiEo=
+github.com/prometheus/client_model v0.6.3/go.mod 
h1:gpN5P9S7Rr6Yr92PiQ+Ixvhf6JZEkF1dnxsYL2aPBEM=
 github.com/prometheus/common v0.70.1 
h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY=
 github.com/prometheus/common v0.70.1/go.mod 
h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc=
 github.com/prometheus/procfs v0.21.1 
h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
@@ -387,8 +390,8 @@
 github.com/valyala/fastjson v1.6.10 
h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4=
 github.com/valyala/fastjson v1.6.10/go.mod 
h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE=
 github.com/vektah/gqlparser v1.2.0/go.mod 
h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74=
-github.com/vektah/gqlparser/v2 v2.5.36 
h1:CN9mKVHgMkc+XftdOWIhb4HEL8wKSYkFAqhf8booa7s=
-github.com/vektah/gqlparser/v2 v2.5.36/go.mod 
h1:cAJ9qwVgPaUkWv6Gn8vn0mqOE0Ui5Pn56wNy5396XWo=
+github.com/vektah/gqlparser/v2 v2.5.37 
h1:jbb1Ilv+xBklV6653tKb4oVUupPNTLb5LmrnBKVI12Y=
+github.com/vektah/gqlparser/v2 v2.5.37/go.mod 
h1:9O4Ox6Ngd3Y12bMD3w6i3CRQXh8W1oC1q0m6olCymDM=
 github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod 
h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
 github.com/vmihailenco/tagparser v0.1.1/go.mod 
h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
 github.com/willf/bitset v1.1.10/go.mod 
h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
@@ -439,8 +442,8 @@
 golang.org/x/crypto v0.56.0/go.mod 
h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
 golang.org/x/exp v0.0.0-20260603202125-055de637280b 
h1:v1uXiEBHo8QA0LiGCo7UgHMzHT4Kdfpl2zmtH5vaP1Q=
 golang.org/x/exp v0.0.0-20260603202125-055de637280b/go.mod 
h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw=
-golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
-golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE=
+golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
+golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod 
h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -449,21 +452,21 @@
 golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
 golang.org/x/oauth2 v0.36.0/go.mod 
h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
-golang.org/x/sync v0.22.0/go.mod 
h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
+golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
+golang.org/x/sync v0.23.0/go.mod 
h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
-golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
+golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
-golang.org/x/text v0.41.0/go.mod 
h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
-golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
-golang.org/x/time v0.15.0/go.mod 
h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
+golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
+golang.org/x/text v0.42.0/go.mod 
h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
+golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE=
+golang.org/x/time v0.16.0/go.mod 
h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/output/azuredevops.go 
new/conftest-0.71.0/output/azuredevops.go
--- old/conftest-0.70.1/output/azuredevops.go   2026-09-19 18:32:18.000000000 
+0200
+++ new/conftest-0.71.0/output/azuredevops.go   2026-09-30 03:15:00.000000000 
+0200
@@ -25,76 +25,35 @@
 
 // Output outputs the results.
 func (t *AzureDevOps) Output(checkResults CheckResults) error {
-       var totalFailures int
-       var totalExceptions int
-       var totalWarnings int
-       var totalSuccesses int
-       var totalSkipped int
-
        for _, result := range checkResults {
-               totalPolicies := result.Successes + len(result.Failures) + 
len(result.Warnings) + len(result.Exceptions) + len(result.Skipped)
+               totalPolicies := result.Totals().Tests()
 
-               fmt.Fprintf(t.writer, "##[section]Testing '%v' against %v 
policies in namespace '%v'\n", result.FileName, totalPolicies, result.Namespace)
+               fmt.Fprintf(t.writer, "##[section]Testing '%s' against %d 
policies in namespace '%s'\n", result.FileName, totalPolicies, result.Namespace)
                fmt.Fprintf(t.writer, "##[group]See conftest results\n")
                for _, failure := range result.Failures {
-                       fmt.Fprintf(t.writer, "##vso[task.logissue type=error] 
file=%v --> %v\n", result.FileName, failure.Message)
+                       fmt.Fprintf(t.writer, "##vso[task.logissue type=error] 
file=%s --> %s\n", result.FileName, failure.Message)
                }
 
                for _, warning := range result.Warnings {
-                       fmt.Fprintf(t.writer, "##vso[task.logissue 
type=warning] file=%v --> %v\n", result.FileName, warning.Message)
+                       fmt.Fprintf(t.writer, "##vso[task.logissue 
type=warning] file=%s --> %s\n", result.FileName, warning.Message)
                }
 
                for _, exception := range result.Exceptions {
-                       fmt.Fprintf(t.writer, "##vso[task.logissue 
type=warning] file=%v --> %v\n", result.FileName, exception.Message)
+                       fmt.Fprintf(t.writer, "##vso[task.logissue 
type=warning] file=%s --> %s\n", result.FileName, exception.Message)
                }
 
                for _, skipped := range result.Skipped {
-                       fmt.Fprintf(t.writer, "skipped file=%v %v\n", 
result.FileName, skipped.Message)
+                       fmt.Fprintf(t.writer, "skipped file=%s %s\n", 
result.FileName, skipped.Message)
                }
 
                if result.Successes > 0 {
-                       fmt.Fprintf(t.writer, "success file=%v %v\n", 
result.FileName, result.Successes)
+                       fmt.Fprintf(t.writer, "success file=%s %d\n", 
result.FileName, result.Successes)
                }
 
-               totalFailures += len(result.Failures)
-               totalExceptions += len(result.Exceptions)
-               totalWarnings += len(result.Warnings)
-               totalSkipped += len(result.Skipped)
-               totalSuccesses += result.Successes
-
                fmt.Fprintf(t.writer, "##[endgroup]\n")
        }
 
-       totalTests := totalFailures + totalExceptions + totalWarnings + 
totalSuccesses + totalSkipped
-
-       var pluralSuffixTests string
-       if totalTests != 1 {
-               pluralSuffixTests = "s"
-       }
-
-       var pluralSuffixWarnings string
-       if totalWarnings != 1 {
-               pluralSuffixWarnings = "s"
-       }
-
-       var pluralSuffixFailures string
-       if totalFailures != 1 {
-               pluralSuffixFailures = "s"
-       }
-
-       var pluralSuffixExceptions string
-       if totalExceptions != 1 {
-               pluralSuffixExceptions = "s"
-       }
-
-       outputText := fmt.Sprintf("%v test%s, %v passed, %v warning%s, %v 
failure%s, %v exception%s",
-               totalTests, pluralSuffixTests,
-               totalSuccesses,
-               totalWarnings, pluralSuffixWarnings,
-               totalFailures, pluralSuffixFailures,
-               totalExceptions, pluralSuffixExceptions,
-       )
-       fmt.Fprintln(t.writer, outputText)
+       fmt.Fprintln(t.writer, checkResults.Totals())
 
        return nil
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/output/github.go 
new/conftest-0.71.0/output/github.go
--- old/conftest-0.70.1/output/github.go        2026-09-19 18:32:18.000000000 
+0200
+++ new/conftest-0.71.0/output/github.go        2026-09-30 03:15:00.000000000 
+0200
@@ -42,25 +42,14 @@
 
 // Output outputs the results.
 func (g *GitHub) Output(checkResults CheckResults) error {
-       var totalFailures int
-       var totalExceptions int
-       var totalWarnings int
-       var totalSuccesses int
-       var totalSkipped int
        for _, result := range checkResults {
-               totalFailures += len(result.Failures)
-               totalExceptions += len(result.Exceptions)
-               totalWarnings += len(result.Warnings)
-               totalSkipped += len(result.Skipped)
-               totalSuccesses += result.Successes
-
                // When hidePassed is set, skip files that only have successful 
checks.
-               // Their tests still count toward the summary line tallied 
above.
+               // Their tests still count toward the summary line.
                if g.hidePassed && len(result.Failures) == 0 && 
len(result.Warnings) == 0 && len(result.Exceptions) == 0 && len(result.Skipped) 
== 0 {
                        continue
                }
 
-               numPolicies := result.Successes + len(result.Failures) + 
len(result.Warnings) + len(result.Exceptions) + len(result.Skipped)
+               numPolicies := result.Totals().Tests()
 
                fileLoc := &Location{File: result.FileName, Line: 
json.Number("1")}
 
@@ -81,15 +70,7 @@
                g.writeLn("::endgroup::")
        }
 
-       totalTests := totalFailures + totalExceptions + totalWarnings + 
totalSuccesses + totalSkipped
-
-       g.writeLn("%d %s, %d passed, %d %s, %d %s, %d %s",
-               totalTests, plural("test", totalTests),
-               totalSuccesses,
-               totalWarnings, plural("warning", totalWarnings),
-               totalFailures, plural("failure", totalFailures),
-               totalExceptions, plural("exception", totalExceptions),
-       )
+       g.writeLn("%s", checkResults.Totals())
 
        return nil
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/output/result.go 
new/conftest-0.71.0/output/result.go
--- old/conftest-0.70.1/output/result.go        2026-09-19 18:32:18.000000000 
+0200
+++ new/conftest-0.71.0/output/result.go        2026-09-30 03:15:00.000000000 
+0200
@@ -212,3 +212,60 @@
        }
        return 0
 }
+
+// Totals holds the number of results of each type.
+type Totals struct {
+       Successes  int
+       Failures   int
+       Warnings   int
+       Exceptions int
+       Skipped    int
+}
+
+// Totals returns the number of results of each type in the check.
+func (cr CheckResult) Totals() Totals {
+       return Totals{
+               Successes:  cr.Successes,
+               Failures:   len(cr.Failures),
+               Warnings:   len(cr.Warnings),
+               Exceptions: len(cr.Exceptions),
+               Skipped:    len(cr.Skipped),
+       }
+}
+
+// Totals returns the number of results of each type across all of the checks.
+func (cr CheckResults) Totals() Totals {
+       var totals Totals
+       for _, result := range cr {
+               totals = totals.Add(result.Totals())
+       }
+       return totals
+}
+
+// Add returns the sum of the two totals.
+func (t Totals) Add(o Totals) Totals {
+       return Totals{
+               Successes:  t.Successes + o.Successes,
+               Failures:   t.Failures + o.Failures,
+               Warnings:   t.Warnings + o.Warnings,
+               Exceptions: t.Exceptions + o.Exceptions,
+               Skipped:    t.Skipped + o.Skipped,
+       }
+}
+
+// Tests returns the total number of tests.
+func (t Totals) Tests() int {
+       return t.Successes + t.Failures + t.Warnings + t.Exceptions + t.Skipped
+}
+
+// String returns the summary line for the totals. Skipped
+// results are counted as tests, but are not listed.
+func (t Totals) String() string {
+       return fmt.Sprintf("%d %s, %d passed, %d %s, %d %s, %d %s",
+               t.Tests(), plural("test", t.Tests()),
+               t.Successes,
+               t.Warnings, plural("warning", t.Warnings),
+               t.Failures, plural("failure", t.Failures),
+               t.Exceptions, plural("exception", t.Exceptions),
+       )
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/output/result_test.go 
new/conftest-0.71.0/output/result_test.go
--- old/conftest-0.70.1/output/result_test.go   2026-09-19 18:32:18.000000000 
+0200
+++ new/conftest-0.71.0/output/result_test.go   2026-09-30 03:15:00.000000000 
+0200
@@ -234,3 +234,66 @@
                })
        }
 }
+
+func TestTotals(t *testing.T) {
+       t.Parallel()
+
+       mixed := CheckResult{
+               Successes:  3,
+               Failures:   []Result{{}, {}},
+               Warnings:   []Result{{}},
+               Exceptions: []Result{{}},
+               Skipped:    []Result{{}},
+       }
+
+       failure := CheckResult{
+               Failures: []Result{{}},
+       }
+
+       testCases := []struct {
+               results       CheckResults
+               expected      Totals
+               expectedTests int
+               expectedText  string
+       }{
+               {
+                       results:      CheckResults{},
+                       expectedText: "0 tests, 0 passed, 0 warnings, 0 
failures, 0 exceptions",
+               },
+               {
+                       results:       CheckResults{failure},
+                       expected:      Totals{Failures: 1},
+                       expectedTests: 1,
+                       expectedText:  "1 test, 0 passed, 0 warnings, 1 
failure, 0 exceptions",
+               },
+               {
+                       results:       CheckResults{mixed},
+                       expected:      Totals{Successes: 3, Failures: 2, 
Warnings: 1, Exceptions: 1, Skipped: 1},
+                       expectedTests: 8,
+                       expectedText:  "8 tests, 3 passed, 1 warning, 2 
failures, 1 exception",
+               },
+               {
+                       results:       CheckResults{mixed, failure, mixed},
+                       expected:      Totals{Successes: 6, Failures: 5, 
Warnings: 2, Exceptions: 2, Skipped: 2},
+                       expectedTests: 17,
+                       expectedText:  "17 tests, 6 passed, 2 warnings, 5 
failures, 2 exceptions",
+               },
+       }
+
+       for i, testCase := range testCases {
+               t.Run(strconv.Itoa(i), func(t *testing.T) {
+                       t.Parallel()
+
+                       actual := testCase.results.Totals()
+                       if actual != testCase.expected {
+                               t.Errorf("Unexpected totals. expected %+v, 
actual %+v", testCase.expected, actual)
+                       }
+                       if actual.Tests() != testCase.expectedTests {
+                               t.Errorf("Unexpected number of tests. expected 
%v, actual %v", testCase.expectedTests, actual.Tests())
+                       }
+                       if actual.String() != testCase.expectedText {
+                               t.Errorf("Unexpected summary. expected %q, 
actual %q", testCase.expectedText, actual.String())
+                       }
+               })
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/output/standard.go 
new/conftest-0.71.0/output/standard.go
--- old/conftest-0.70.1/output/standard.go      2026-09-19 18:32:18.000000000 
+0200
+++ new/conftest-0.71.0/output/standard.go      2026-09-30 03:15:00.000000000 
+0200
@@ -58,11 +58,6 @@
 
        s.outputPrints(results, colorizer)
 
-       var totalFailures int
-       var totalExceptions int
-       var totalWarnings int
-       var totalSuccesses int
-       var totalSkipped int
        for _, result := range results {
                var indicator string
                var namespace string
@@ -91,54 +86,21 @@
                                fmt.Fprintln(s.Writer, 
colorizer.Colorize("EXCP", aurora.CyanFg), indicator, namespace, 
exception.Message)
                        }
                }
-
-               totalFailures += len(result.Failures)
-               totalExceptions += len(result.Exceptions)
-               totalWarnings += len(result.Warnings)
-               totalSkipped += len(result.Skipped)
-               totalSuccesses += result.Successes
-       }
-
-       totalTests := totalFailures + totalExceptions + totalWarnings + 
totalSuccesses + totalSkipped
-
-       var pluralSuffixTests string
-       if totalTests != 1 {
-               pluralSuffixTests = "s"
-       }
-
-       var pluralSuffixWarnings string
-       if totalWarnings != 1 {
-               pluralSuffixWarnings = "s"
-       }
-
-       var pluralSuffixFailures string
-       if totalFailures != 1 {
-               pluralSuffixFailures = "s"
-       }
-
-       var pluralSuffixExceptions string
-       if totalExceptions != 1 {
-               pluralSuffixExceptions = "s"
        }
 
-       outputText := fmt.Sprintf("%v test%s, %v passed, %v warning%s, %v 
failure%s, %v exception%s",
-               totalTests, pluralSuffixTests,
-               totalSuccesses,
-               totalWarnings, pluralSuffixWarnings,
-               totalFailures, pluralSuffixFailures,
-               totalExceptions, pluralSuffixExceptions,
-       )
+       totals := results.Totals()
 
+       outputText := totals.String()
        if s.ShowSkipped {
-               outputText += fmt.Sprintf(", %v skipped", totalSkipped)
+               outputText += fmt.Sprintf(", %d skipped", totals.Skipped)
        }
 
        var outputColor aurora.Color
-       if totalFailures > 0 {
+       if totals.Failures > 0 {
                outputColor = aurora.RedFg
-       } else if totalWarnings > 0 {
+       } else if totals.Warnings > 0 {
                outputColor = aurora.YellowFg
-       } else if totalExceptions > 0 {
+       } else if totals.Exceptions > 0 {
                outputColor = aurora.CyanFg
        } else {
                outputColor = aurora.GreenFg
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/output/tap.go 
new/conftest-0.71.0/output/tap.go
--- old/conftest-0.70.1/output/tap.go   2026-09-19 18:32:18.000000000 +0200
+++ new/conftest-0.71.0/output/tap.go   2026-09-30 03:15:00.000000000 +0200
@@ -3,6 +3,7 @@
 import (
        "fmt"
        "io"
+       "slices"
 
        "github.com/open-policy-agent/opa/v1/tester"
 )
@@ -24,6 +25,16 @@
 
 // Output outputs the results.
 func (t *TAP) Output(checkResults CheckResults) error {
+       // TAP expects a single plan for the whole stream, with test numbers
+       // continuing across all of the checks.
+       totalTests := checkResults.Totals().Tests()
+       if totalTests == 0 {
+               return nil
+       }
+
+       fmt.Fprintf(t.Writer, "1..%d\n", totalTests)
+
+       counter := 1
        for _, result := range checkResults {
                var indicator string
                var namespace string
@@ -39,47 +50,24 @@
                        namespace = fmt.Sprintf("- %s -", result.Namespace)
                }
 
-               totalTests := result.Successes + len(result.Failures) + 
len(result.Warnings) + len(result.Exceptions) + len(result.Skipped)
-               if totalTests == 0 {
-                       return nil
+               sections := []struct {
+                       header  string
+                       status  string
+                       results []Result
+               }{
+                       {status: "not ok", results: result.Failures},
+                       {header: "warnings", status: "not ok", results: 
result.Warnings},
+                       {header: "exceptions", status: "ok", results: 
result.Exceptions},
+                       {header: "skip", status: "ok", results: result.Skipped},
+                       {header: "successes", status: "ok", results: 
slices.Repeat([]Result{{Message: "SUCCESS"}}, result.Successes)},
                }
 
-               counter := 1
-               fmt.Fprintf(t.Writer, "1..%d\n", totalTests)
-
-               for _, failure := range result.Failures {
-                       fmt.Fprintf(t.Writer, "not ok %v %v %v %v\n", counter, 
indicator, namespace, failure.Message)
-                       counter++
-               }
-
-               if len(result.Warnings) > 0 {
-                       fmt.Fprintln(t.Writer, "# warnings")
-                       for _, warning := range result.Warnings {
-                               fmt.Fprintf(t.Writer, "not ok %v %v %v %v\n", 
counter, indicator, namespace, warning.Message)
-                               counter++
+               for _, section := range sections {
+                       if section.header != "" && len(section.results) > 0 {
+                               fmt.Fprintln(t.Writer, "#", section.header)
                        }
-               }
-
-               if len(result.Exceptions) > 0 {
-                       fmt.Fprintln(t.Writer, "# exceptions")
-                       for _, exception := range result.Exceptions {
-                               fmt.Fprintf(t.Writer, "ok %v %v %v %v\n", 
counter, indicator, namespace, exception.Message)
-                               counter++
-                       }
-               }
-
-               if len(result.Skipped) > 0 {
-                       fmt.Fprintln(t.Writer, "# skip")
-                       for _, skipped := range result.Skipped {
-                               fmt.Fprintf(t.Writer, "ok %v %v %v %v\n", 
counter, indicator, namespace, skipped.Message)
-                               counter++
-                       }
-               }
-
-               if result.Successes > 0 {
-                       fmt.Fprintln(t.Writer, "# successes")
-                       for i := 0; i < result.Successes; i++ {
-                               fmt.Fprintf(t.Writer, "ok %v %v %v %v\n", 
counter, indicator, namespace, "SUCCESS")
+                       for _, r := range section.results {
+                               fmt.Fprintf(t.Writer, "%s %d %s %s %s\n", 
section.status, counter, indicator, namespace, r.Message)
                                counter++
                        }
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/output/tap_test.go 
new/conftest-0.71.0/output/tap_test.go
--- old/conftest-0.70.1/output/tap_test.go      2026-09-19 18:32:18.000000000 
+0200
+++ new/conftest-0.71.0/output/tap_test.go      2026-09-30 03:15:00.000000000 
+0200
@@ -41,6 +41,50 @@
                        },
                },
                {
+                       name: "multiple files use a single plan with continuous 
numbering",
+                       input: CheckResults{
+                               {
+                                       FileName:  
"examples/kubernetes/service.yaml",
+                                       Namespace: "namespace",
+                                       Failures:  []Result{{Message: "first 
failure"}},
+                               },
+                               {
+                                       FileName:  
"examples/kubernetes/deployment.yaml",
+                                       Namespace: "namespace",
+                                       Warnings:  []Result{{Message: "second 
file warning"}},
+                                       Successes: 1,
+                               },
+                       },
+                       expected: []string{
+                               "1..3",
+                               "not ok 1 - examples/kubernetes/service.yaml - 
namespace - first failure",
+                               "# warnings",
+                               "not ok 2 - examples/kubernetes/deployment.yaml 
- namespace - second file warning",
+                               "# successes",
+                               "ok 3 - examples/kubernetes/deployment.yaml - 
namespace - SUCCESS",
+                               "",
+                       },
+               },
+               {
+                       name: "files without tests do not truncate later 
results",
+                       input: CheckResults{
+                               {
+                                       FileName:  
"examples/kubernetes/service.yaml",
+                                       Namespace: "namespace",
+                               },
+                               {
+                                       FileName:  
"examples/kubernetes/deployment.yaml",
+                                       Namespace: "namespace",
+                                       Failures:  []Result{{Message: "first 
failure"}},
+                               },
+                       },
+                       expected: []string{
+                               "1..1",
+                               "not ok 1 - examples/kubernetes/deployment.yaml 
- namespace - first failure",
+                               "",
+                       },
+               },
+               {
                        name: "mixed failure, warnings and skipped",
                        input: CheckResults{
                                {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/parser/codeowners/codeowners.go 
new/conftest-0.71.0/parser/codeowners/codeowners.go
--- old/conftest-0.70.1/parser/codeowners/codeowners.go 1970-01-01 
01:00:00.000000000 +0100
+++ new/conftest-0.71.0/parser/codeowners/codeowners.go 2026-09-30 
03:15:00.000000000 +0200
@@ -0,0 +1,114 @@
+// Package codeowners provides a parser for CODEOWNERS files
+//
+// A [CODEOWNERS] file defines which reviewers are required for various
+// files within a repository. The file format is similar to
+// [gitignore].
+//
+// GitHub searches for the CODEOWNERS file in the following priority
+// order relative to the repository root:
+//   - .github/CODEOWNERS
+//   - CODEOWNERS
+//   - docs/CODEOWNERS
+//
+// [CODEOWNERS]: 
https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
+// [gitignore]: https://git-scm.com/docs/gitignore
+package codeowners
+
+import (
+       "bytes"
+       "encoding/json"
+       "fmt"
+
+       "github.com/hmarr/codeowners"
+)
+
+// codeowners contains required reviewers for zero or more file patterns.
+type owners struct {
+       Rules []rule `json:"rules"`
+}
+
+// rule contains the required reviewers for a file pattern.
+type rule struct {
+       Pattern    string  `json:"pattern"`
+       Owners     []owner `json:"owners"`
+       Comment    string  `json:"comment"`
+       LineNumber int     `json:"lineNumber"`
+}
+
+type ownerType string
+
+const (
+       // EmailOwner is the owner type for email addresses.
+       emailOwner ownerType = "email"
+       // TeamOwner is the owner type for GitHub teams.
+       teamOwner ownerType = "team"
+       // UsernameOwner is the owner type for GitHub usernames.
+       usernameOwner ownerType = "username"
+)
+
+// owner contains information about a codeowner
+type owner struct {
+       Type  ownerType `json:"type"`
+       Value string    `json:"value"`
+}
+
+func ownerFromEmail(email string) owner {
+       return owner{
+               Type:  emailOwner,
+               Value: email,
+       }
+}
+
+func ownerFromTeam(team string) owner {
+       return owner{
+               Type:  teamOwner,
+               Value: team,
+       }
+}
+
+func ownerFromUsername(username string) owner {
+       return owner{
+               Type:  usernameOwner,
+               Value: username,
+       }
+}
+
+// Parser is a CODEOWNERS file parser.
+type Parser struct{}
+
+// Unmarshal unmarshals a CODEOWNERS file.
+func (p *Parser) Unmarshal(data []byte, v any) error {
+       f := bytes.NewBuffer(data)
+       ruleset, err := codeowners.ParseFile(f)
+       if err != nil {
+               return fmt.Errorf("failed to parse CODEOWNERS: %w", err)
+       }
+
+       config := owners{}
+       for _, r := range ruleset {
+               os := make([]owner, len(r.Owners))
+               for i, o := range r.Owners {
+                       os[i] = owner{
+                               Type:  ownerType(o.Type),
+                               Value: o.Value,
+                       }
+               }
+               config.Rules = append(config.Rules, rule{
+                       Pattern:    r.RawPattern(),
+                       Owners:     os,
+                       Comment:    r.Comment,
+                       LineNumber: r.LineNumber,
+               })
+       }
+
+       j, err := json.Marshal(config)
+       if err != nil {
+               return fmt.Errorf("marshal CODEOWNERS to json: %w", err)
+       }
+
+       if err := json.Unmarshal(j, v); err != nil {
+               return fmt.Errorf("unmarshal CODEOWNERS json: %w", err)
+       }
+
+       return nil
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/parser/codeowners/codeowners_test.go 
new/conftest-0.71.0/parser/codeowners/codeowners_test.go
--- old/conftest-0.70.1/parser/codeowners/codeowners_test.go    1970-01-01 
01:00:00.000000000 +0100
+++ new/conftest-0.71.0/parser/codeowners/codeowners_test.go    2026-09-30 
03:15:00.000000000 +0200
@@ -0,0 +1,247 @@
+package codeowners
+
+import (
+       "encoding/json"
+       "fmt"
+       "testing"
+)
+
+const codePath = "/apps/code.go"
+
+var (
+       bobEmail       = ownerFromEmail("[email protected]")
+       bobUser        = ownerFromUsername("bob")
+       carolUser      = ownerFromUsername("carol")
+       developersTeam = ownerFromTeam("my-org/developers")
+       librariansTeam = ownerFromTeam("my-org/librarians")
+       goOwner        = ownerFromUsername("go-owner")
+)
+
+func TestParser(t *testing.T) {
+       tests := []struct {
+               name    string
+               input   []byte
+               want    owners
+               wantErr bool
+       }{
+               {
+                       name: "anyone with write access",
+                       // A lone pattern requires a review from anyone with 
write access
+                       input: []byte(codePath),
+                       want: owners{
+                               Rules: []rule{
+                                       {
+                                               Pattern: codePath,
+                                               Owners:  []owner{},
+                                       },
+                               },
+                       },
+               },
+               {
+                       name:  "comment",
+                       input: []byte(`# Just a comment`),
+                       want: owners{
+                               Rules: []rule{},
+                       },
+               },
+               {
+                       name:  "user owner",
+                       input: []byte(`* @bob`),
+                       want: owners{
+                               Rules: []rule{
+                                       {
+                                               Pattern: "*",
+                                               Owners:  []owner{bobUser},
+                                       },
+                               },
+                       },
+               },
+               {
+                       name:  "email owner",
+                       input: []byte(`*.go [email protected]`),
+                       want: owners{
+                               Rules: []rule{
+                                       {
+                                               Pattern: "*.go",
+                                               Owners:  []owner{bobEmail},
+                                       },
+                               },
+                       },
+               },
+               {
+                       name:  "team owner",
+                       input: []byte(`*.txt @my-org/developers`),
+                       want: owners{
+                               Rules: []rule{
+                                       {
+                                               Pattern: "*.txt",
+                                               Owners:  
[]owner{developersTeam},
+                                       },
+                               },
+                       },
+               },
+               {
+                       name:  "two owners",
+                       input: []byte(`* @bob @carol`),
+                       want: owners{
+                               Rules: []rule{
+                                       {
+                                               Pattern: "*",
+                                               Owners:  []owner{bobUser, 
carolUser},
+                                       },
+                               },
+                       },
+               },
+               {
+                       name:  "inline comment",
+                       input: []byte(`*.js    @carol #This is an inline 
comment.`),
+                       want: owners{
+                               Rules: []rule{
+                                       {
+                                               Pattern: "*.js",
+                                               Owners:  []owner{carolUser},
+                                       },
+                               },
+                       },
+               },
+               {
+                       name:  "folder path",
+                       input: []byte(`/build/logs/ @my-org/librarians`),
+                       want: owners{
+                               Rules: []rule{
+                                       {
+                                               Pattern: "/build/logs/",
+                                               Owners:  
[]owner{librariansTeam},
+                                       },
+                               },
+                       },
+               },
+               {
+                       name: "multi-line example",
+                       input: []byte(`# This is a comment.
+# Default owner
+*       @my-org/developers
+
+# Special owner for go files
+*.go @go-owner
+
+*.txt @bob
+
+docs/ @my-org/librarians
+
+/scripts/ @bob @carol
+`),
+                       want: owners{
+                               Rules: []rule{
+                                       {
+                                               Pattern: "*",
+                                               Owners:  
[]owner{developersTeam},
+                                       },
+                                       {
+                                               Pattern: "*.go",
+                                               Owners:  []owner{goOwner},
+                                       },
+                                       {
+                                               Pattern: "*.txt",
+                                               Owners:  []owner{bobUser},
+                                       },
+                                       {
+                                               Pattern: "docs/",
+                                               Owners:  
[]owner{librariansTeam},
+                                       },
+                                       {
+                                               Pattern: "/scripts/",
+                                               Owners:  []owner{bobUser, 
carolUser},
+                                       },
+                               },
+                       },
+               },
+               {
+                       name:  "windows line endings",
+                       input: []byte("# developers by default\r\n* 
@my-org/developers\r\n# librarians for docs\r\ndocs/\t@my-org/librarians\r\n"),
+                       want: owners{
+                               Rules: []rule{
+                                       {
+                                               Pattern: "*",
+                                               Owners:  
[]owner{developersTeam},
+                                       },
+                                       {
+                                               Pattern: "docs/",
+                                               Owners:  
[]owner{librariansTeam},
+                                       },
+                               },
+                       },
+               },
+       }
+
+       parser := &Parser{}
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       var parsedInput any
+                       err := parser.Unmarshal(tt.input, &parsedInput)
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("Unmarshal() error = %v, wantErr %v", 
err, tt.wantErr)
+                               return
+                       }
+                       if !tt.wantErr && parsedInput == nil {
+                               t.Fatal("expected parsed content, got nil")
+                       }
+
+                       got := roundTrip(t, parsedInput)
+                       if err != nil {
+                               t.Fatalf("round trip of CODEOWNERS failed: %v", 
err)
+                       }
+                       expectCodeowners(t, got, tt.want)
+               })
+       }
+}
+
+func roundTrip(t *testing.T, parsed any) owners {
+       t.Helper()
+       ownersJSON, err := json.Marshal(parsed)
+       if err != nil {
+               t.Fatalf("marshal CODEOWNERS to JSON: %v", err)
+       }
+       var output owners
+       if err := json.Unmarshal(ownersJSON, &output); err != nil {
+               t.Fatalf("unmarshal CODEOWNERS JSON: %v", err)
+       }
+       return output
+}
+
+func expectCodeowners(t *testing.T, got, want owners) {
+       t.Helper()
+       if len(got.Rules) != len(want.Rules) {
+               t.Errorf("expected %d rules, got %d rules", len(want.Rules), 
len(got.Rules))
+               return
+       }
+       for i, wantRule := range want.Rules {
+               gotRule := got.Rules[i]
+               expectRule(t, gotRule, wantRule)
+       }
+}
+
+func expectRule(t *testing.T, got, want rule) {
+       t.Helper()
+       if got.Pattern != want.Pattern {
+               t.Errorf("line %d: expected '%s' pattern, got '%s'", 
got.LineNumber, want.Pattern, got.Pattern)
+       }
+       if len(got.Owners) != len(want.Owners) {
+               t.Errorf("line %d: expected %d owners, got %d owners", 
got.LineNumber, len(want.Owners), len(got.Owners))
+               return
+       }
+       for i, wantOwner := range want.Owners {
+               gotOwner := got.Owners[i]
+               err := expectOwner(gotOwner, wantOwner)
+               if err != nil {
+                       t.Errorf("line %d: owner %d: %v", got.LineNumber, i, 
err)
+               }
+       }
+}
+
+func expectOwner(got, want owner) error {
+       if got.Type != want.Type || got.Value != want.Value {
+               return fmt.Errorf("expected: %s %s, got %s %s", want.Type, 
want.Value, got.Type, got.Value)
+       }
+       return nil
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/parser/parser.go 
new/conftest-0.71.0/parser/parser.go
--- old/conftest-0.70.1/parser/parser.go        2026-09-19 18:32:18.000000000 
+0200
+++ new/conftest-0.71.0/parser/parser.go        2026-09-30 03:15:00.000000000 
+0200
@@ -13,6 +13,7 @@
        "github.com/spf13/viper"
        "golang.org/x/exp/slices"
 
+       "github.com/open-policy-agent/conftest/parser/codeowners"
        "github.com/open-policy-agent/conftest/parser/cue"
        "github.com/open-policy-agent/conftest/parser/cyclonedx"
        "github.com/open-policy-agent/conftest/parser/docker"
@@ -40,6 +41,7 @@
 // The defined parsers are the parsers that are valid for
 // parsing files.
 const (
+       CODEOWNERS = "codeowners"
        CUE        = "cue"
        CYCLONEDX  = "cyclonedx"
        Dockerfile = "dockerfile"
@@ -135,6 +137,8 @@
                }
 
                return parser, nil
+       case CODEOWNERS:
+               return &codeowners.Parser{}, nil
        default:
                return nil, fmt.Errorf("unknown parser: %v", parser)
        }
@@ -221,6 +225,10 @@
                return New(TEXTPROTO)
        }
 
+       if fileName == "codeowners" {
+               return New(CODEOWNERS)
+       }
+
        parser, err := New(fileExtension)
        if err != nil {
                return nil, fmt.Errorf("new: %w", err)
@@ -232,6 +240,7 @@
 // Parsers returns a list of the supported Parsers.
 func Parsers() []string {
        parsers := []string{
+               CODEOWNERS,
                CUE,
                CYCLONEDX,
                Dockerfile,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/requirements.txt 
new/conftest-0.71.0/requirements.txt
--- old/conftest-0.70.1/requirements.txt        2026-09-19 18:32:18.000000000 
+0200
+++ new/conftest-0.71.0/requirements.txt        2026-09-30 03:15:00.000000000 
+0200
@@ -1,2 +1,2 @@
-mkdocs==1.3.0
-mkdocs-material==4.6.3
+mkdocs==1.6.1
+mkdocs-material==9.7.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/conftest-0.70.1/runtime.txt 
new/conftest-0.71.0/runtime.txt
--- old/conftest-0.70.1/runtime.txt     2026-09-19 18:32:18.000000000 +0200
+++ new/conftest-0.71.0/runtime.txt     2026-09-30 03:15:00.000000000 +0200
@@ -1 +1 @@
-3.8
\ No newline at end of file
+3.12

++++++ conftest.obsinfo ++++++
--- /var/tmp/diff_new_pack.KWT4EC/_old  2026-10-06 19:17:48.874021256 +0200
+++ /var/tmp/diff_new_pack.KWT4EC/_new  2026-10-06 19:17:48.878021424 +0200
@@ -1,5 +1,5 @@
 name: conftest
-version: 0.70.1
-mtime: 1789835538
-commit: 0b42cb5cd1aa1dc1c9025a5e83c7e963166d08c0
+version: 0.71.0
+mtime: 1790730900
+commit: 8345c40e21f3e6ab1159f4e15585b5f7d6aa32a7
 

++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/conftest/vendor.tar.gz 
/work/SRC/openSUSE:Factory/.conftest.new.3698736/vendor.tar.gz differ: char 13, 
line 1

Reply via email to