Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Resource quantities (pull_request) Successful in 10s
Validate Helm Charts / Selector match (pull_request) Successful in 9s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 9s
Validate Helm Charts / NetworkPolicy (pull_request) Successful in 12s
Validate Helm Charts / Bump level (pull_request) Successful in 4s
Validate Helm Charts / YAML scalars (pull_request) Successful in 10s
Validate Helm Charts / Build paperless-ngx (pull_request) Successful in 6s
Validate Helm Charts / Trivy config paperless-ngx (pull_request) Successful in 20s
Install-test / Install-test paperless-ngx (pull_request) Successful in 3m2s
Validate Helm Charts / Install-test (pull_request) Successful in 3m2s
The four assertions from Gitea #5 hold ports against the pod spec the policy SELECTS. That is right for ingress and meaningless for egress: an egress port belongs to the destination, and for a database, a cache or the web that destination is in no render this repository has. So the mistake behind #136 - an egress rule naming a ClusterIP Service's port, which kube-proxy has already translated away by the time the policy is evaluated at the pod's interface - was not catchable within one chart. It is catchable across two, and paperless-ngx -> apache-tika, gotenberg is the only place in the repository where a chart dials another chart's Service (the three other policies naming port 80 aim at the internet, where there is no DNAT and 80 is the real port). The pairs are an explicit table rather than a discovery, because no render can know which Service another chart dials - the address is a value or a 1Password field - and a row naming a chart that no longer exists fails instead of being skipped. Each row is checked on two renders of the consumer: with the feature gate ON the egress ports must CONTAIN every pod port behind the peers' Services, resolved Service port -> targetPort -> containerPort, which is exactly the translation the policy is evaluated after; with the gate OFF the requirement must not apply and those ports must be absent, so a gated rule cannot quietly become one that permits what the release cannot use. The evidence line prints the chain ("port 80 -> targetPort 'tika-port' -> pod port 9998"), so a reader sees the DNAT rather than two numbers. Built into run-networkpolicy-test.sh rather than added as a new script with its own job: that job already runs over every chart on every PR, Helm is already the YAML parser through ci/yaml2json, and the verdict and summary plumbing is shared. python3 standard library only, no yq, no pyyaml, no Docker. Six fail paths executed, not assumed: a required port removed, a wrong number in its place, the peer chart renamed away, the policy name no longer following the <release>-<chart>-<kind> convention, the peers rendering no Service at all (an empty requirement reads like a satisfied one, so it fails), and the rule made unconditional, which is the fail path of the gate-off half. Measured: 30 charts, 210 assertions held (208 before), 0 failed, ~15s, no cluster. README's stated figure was 206 and is corrected here. Part of #136