laskoviymishka commented on code in PR #9: URL: https://github.com/apache/iceberg-verification/pull/9#discussion_r4036951278
########## dev/check-license: ########## @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +acquire_rat_jar () { + + URL="https://repo.maven.apache.org/maven2/org/apache/rat/apache-rat/${RAT_VERSION}/apache-rat-${RAT_VERSION}.jar" + + JAR="$rat_jar" + + # Download rat launch jar if it hasn't been downloaded yet + if [ ! -f "$JAR" ]; then + # Download + printf "Attempting to fetch rat\n" + JAR_DL="${JAR}.part" + if [ $(command -v curl) ]; then + curl -L --silent "${URL}" > "$JAR_DL" && mv "$JAR_DL" "$JAR" Review Comment: We download the RAT jar and only check that it unzips — no checksum or signature. A compromised mirror or MITM could swap in a jar that then executes in CI. I'd verify a pinned SHA-256 (or the `.sha512`/PGP that Maven Central publishes) before running it. Not blocking, but cheap hardening for something we execute. ########## .github/workflows/license_check.yml: ########## @@ -0,0 +1,32 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: "Run License Check" +on: pull_request Review Comment: This one triggers only on `pull_request`, but validate-fixtures also runs on push to `main` — so a direct push to main skips the license check. I'd add `push: branches: [main]` here to match. While we're in here, validate-fixtures has a concurrency group that cancels superseded runs and this one doesn't; worth copying over. ########## dev/check-license: ########## @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +acquire_rat_jar () { + + URL="https://repo.maven.apache.org/maven2/org/apache/rat/apache-rat/${RAT_VERSION}/apache-rat-${RAT_VERSION}.jar" + + JAR="$rat_jar" + + # Download rat launch jar if it hasn't been downloaded yet + if [ ! -f "$JAR" ]; then + # Download + printf "Attempting to fetch rat\n" + JAR_DL="${JAR}.part" + if [ $(command -v curl) ]; then + curl -L --silent "${URL}" > "$JAR_DL" && mv "$JAR_DL" "$JAR" + elif [ $(command -v wget) ]; then + wget --quiet ${URL} -O "$JAR_DL" && mv "$JAR_DL" "$JAR" + else + printf "You do not have curl or wget installed, please install rat manually.\n" + exit -1 Review Comment: `exit -1` isn't portable — exit codes are 0–255, so this lands on 255 on bash but isn't guaranteed and lint tools flag it. I'd use `exit 1` in both spots. ########## dev/schema/cases.base.schema.json: ########## @@ -0,0 +1,46 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://iceberg.apache.org/verification/cases.base.schema.json", + "title": "Conformance cases (base)", + "description": "Structure shared by every table-spec surface's cases.json.", + "type": "object", + "required": ["cases"], + "properties": { + "cases": { + "type": "array", + "items": { "$ref": "#/$defs/case" } + } + }, + "$defs": { + "case": { + "type": "object", Review Comment: I'd set `"additionalProperties": false` on the case object. Right now a typo like `"spec-ref"` or `"decode"` validates clean — the case just silently lacks the field, and a mistyped `decoded` could make a fixture assert the wrong thing. For a schema whose whole job is catching malformed fixtures, I'd want the strict form here (and on the top-level object). ########## table-spec/types/geospatial/cases.json: ########## @@ -0,0 +1,83 @@ +{ + "cases": [ + { + "id": "geometry-crs84", + "valid": true, + "input": "geometry(OGC:CRS84)", + "decoded": {"type": "geometry", "crs": "OGC:CRS84"}, + "clause": "geometry(C) with explicit CRS; the canonical serialized form is unquoted \"geometry(<C>)\"", + "spec_ref": "format/spec.md#appendix-c-json-serialization" + }, + { + "id": "geometry-srid", + "valid": true, + "input": "geometry(srid:4326)", + "decoded": {"type": "geometry", "crs": "srid:4326"}, + "clause": "geometry(C) example from Appendix C is the unquoted \"geometry(srid:4326)\"", + "spec_ref": "format/spec.md#appendix-c-json-serialization" + }, + { + "id": "geometry-default-crs", + "valid": true, + "input": "geometry", + "decoded": {"type": "geometry", "crs": "OGC:CRS84"}, + "clause": "geometry(C): if C is not specified, C is OGC:CRS84", + "spec_ref": "format/spec.md#primitive-types" + }, + { + "id": "geography-crs84-spherical", + "valid": true, + "input": "geography(OGC:CRS84, spherical)", + "decoded": {"type": "geography", "crs": "OGC:CRS84", "algorithm": "spherical"}, + "clause": "geography(C, A); the canonical serialized form is unquoted \"geography(<C>, <A>)\"", + "spec_ref": "format/spec.md#appendix-c-json-serialization" + }, + { + "id": "geography-default", + "valid": true, + "input": "geography", + "decoded": {"type": "geography", "crs": "OGC:CRS84", "algorithm": "spherical"}, + "clause": "geography(C, A): if not specified, C is OGC:CRS84 and A is spherical", Review Comment: There's no case for `geography(OGC:CRS84)` — CRS given, algorithm omitted. The prose says both default when unspecified, but whether the algorithm can be omitted while the CRS is explicit reads as genuinely ambiguous to me. I'd add one case pinning it either way (`valid: true` decoding with `algorithm: spherical`, or `valid: false` if the algorithm is required once CRS is explicit), with a clause citing the sentence that resolves it. How do you read the spec here? ########## table-spec/types/primitive/cases.json: ########## @@ -0,0 +1,33 @@ +{ + "cases": [ + { "id": "boolean", "valid": true, "input": "boolean", "decoded": { "type": "boolean" }, "canonical": "boolean", "clause": "Primitive Types: boolean; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "int", "valid": true, "input": "int", "decoded": { "type": "int" }, "canonical": "int", "clause": "Primitive Types: int; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "long", "valid": true, "input": "long", "decoded": { "type": "long" }, "canonical": "long", "clause": "Primitive Types: long; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "float", "valid": true, "input": "float", "decoded": { "type": "float" }, "canonical": "float", "clause": "Primitive Types: float; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "double", "valid": true, "input": "double", "decoded": { "type": "double" }, "canonical": "double", "clause": "Primitive Types: double; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "date", "valid": true, "input": "date", "decoded": { "type": "date" }, "canonical": "date", "clause": "Primitive Types: date; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "time", "valid": true, "input": "time", "decoded": { "type": "time" }, "canonical": "time", "clause": "Primitive Types: time; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "timestamp", "valid": true, "input": "timestamp", "decoded": { "type": "timestamp" }, "canonical": "timestamp", "clause": "Primitive Types: timestamp; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "timestamptz", "valid": true, "input": "timestamptz", "decoded": { "type": "timestamptz" }, "canonical": "timestamptz", "clause": "Primitive Types: timestamptz; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "timestamp_ns", "valid": true, "input": "timestamp_ns", "decoded": { "type": "timestamp_ns" }, "canonical": "timestamp_ns", "clause": "Primitive Types: timestamp_ns added in v3; Appendix C canonical string", "spec_ref": "format/spec.md#primitive-types" }, + { "id": "timestamptz_ns", "valid": true, "input": "timestamptz_ns", "decoded": { "type": "timestamptz_ns" }, "canonical": "timestamptz_ns", "clause": "Primitive Types: timestamptz_ns added in v3; Appendix C canonical string", "spec_ref": "format/spec.md#primitive-types" }, + { "id": "string", "valid": true, "input": "string", "decoded": { "type": "string" }, "canonical": "string", "clause": "Primitive Types: string; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "uuid", "valid": true, "input": "uuid", "decoded": { "type": "uuid" }, "canonical": "uuid", "clause": "Primitive Types: uuid; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "binary", "valid": true, "input": "binary", "decoded": { "type": "binary" }, "canonical": "binary", "clause": "Primitive Types: binary; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "unknown", "valid": true, "input": "unknown", "decoded": { "type": "unknown" }, "canonical": "unknown", "clause": "Primitive Types: unknown added in v3; Appendix C canonical string", "spec_ref": "format/spec.md#primitive-types" }, + { "id": "fixed-1", "valid": true, "input": "fixed[1]", "decoded": { "type": "fixed", "length": 1 }, "canonical": "fixed[1]", "clause": "Appendix C: fixed canonical string is fixed[<L>]", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "fixed-16", "valid": true, "input": "fixed[16]", "decoded": { "type": "fixed", "length": 16 }, "canonical": "fixed[16]", "clause": "Appendix C: fixed canonical string is fixed[<L>]", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "decimal-9-2", "valid": true, "input": "decimal(9,2)", "decoded": { "type": "decimal", "precision": 9, "scale": 2 }, "clause": "Appendix C: both decimal(9,2) and decimal(9, 2) are canonical, so no byte-exact form is pinned", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, + { "id": "decimal-9-2-spaced", "valid": true, "input": "decimal(9, 2)", "decoded": { "type": "decimal", "precision": 9, "scale": 2 }, "clause": "Appendix C: the spaced decimal(9, 2) form parses to the same decimal", "spec_ref": "format/spec.md#appendix-c-json-serialization" }, Review Comment: moomindani's consumer-side run reframed this one for me: `decimal(9, 2)` is accepted by every implementation they checked, so as written this case is inert — it doesn't separate a strict parser from a lenient one. The input that does is the broader form apache/iceberg#16798 actually added, "whitespace around parameters and separators": PyIceberg rejects `decimal( 9 , 2 )` / `decimal(9 ,2)` while Java accepts them, and both are conformant. That's the case I'd want on this surface (the sungwy/iceberg-testing prototype carried it, noting "a reader stricter than that diverges here"). But because both parsers are conformant on it, it can't be a plain `valid: true` — a MUST-accept would fail PyIceberg for behavior the spec only recommends. So I'd swap in the divergent `decimal( 9 , 2 )` input and give it a normative tier: an optional `normative_level` on the base schema (default `"must"`) with this case marked as the SHOULD, so runners treat it as advisory rather than a hard fail. I'd leave `canonical` off the decimal cases as you have it — agreed the pinned-spelling question is spec text, not something to settle in a fixture. wdyt on the normative tier? ########## .github/workflows/validate-fixtures.yml: ########## @@ -0,0 +1,48 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: "Validate Fixtures" + +on: + push: + branches: + - main + pull_request: + +concurrency: + group: ${{ github.repository }}-${{ github.head_ref || github.sha }}-${{ github.workflow }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + +jobs: + validate: + name: Validate fixture cases + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install Python + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + with: + python-version: '3.12' + - name: Install validator deps + run: python3 -m pip install -r dev/requirements.txt + - name: Validate cases.json + run: python3 dev/validate-fixtures.py Review Comment: CI only ever runs the validator against the real fixtures, which are all well-formed, so nothing proves the validator actually rejects a bad fixture. If schema enforcement broke — a logic bug, or the jsonschema bump changing the error path — CI stays green and the gate is silently worthless. I'd add a step that feeds a deliberately malformed cases.json (say, `valid: true` with no `decoded`, or a types case missing `clause`) and asserts a nonzero exit. Since the validator is the gate for every future contribution, I'd want that guard in this PR. ########## dev/check-license: ########## @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +acquire_rat_jar () { + + URL="https://repo.maven.apache.org/maven2/org/apache/rat/apache-rat/${RAT_VERSION}/apache-rat-${RAT_VERSION}.jar" + + JAR="$rat_jar" + + # Download rat launch jar if it hasn't been downloaded yet + if [ ! -f "$JAR" ]; then + # Download + printf "Attempting to fetch rat\n" + JAR_DL="${JAR}.part" + if [ $(command -v curl) ]; then + curl -L --silent "${URL}" > "$JAR_DL" && mv "$JAR_DL" "$JAR" + elif [ $(command -v wget) ]; then + wget --quiet ${URL} -O "$JAR_DL" && mv "$JAR_DL" "$JAR" + else + printf "You do not have curl or wget installed, please install rat manually.\n" + exit -1 + fi + fi + + unzip -tq "$JAR" &> /dev/null + if [ $? -ne 0 ]; then + # We failed to download + rm "$JAR" + printf "Our attempt to download rat locally to ${JAR} failed. Please install rat manually.\n" + exit -1 + fi +} + +# Go to the project root directory +FWDIR="$(cd "`dirname "$0"`"/..; pwd)" +cd "$FWDIR" + +if test -x "$JAVA_HOME/bin/java"; then + declare java_cmd="$JAVA_HOME/bin/java" +else + declare java_cmd=java +fi + +export RAT_VERSION=0.17 +export rat_jar="$FWDIR"/lib/apache-rat-${RAT_VERSION}.jar Review Comment: The closing quote ends right after `$FWDIR`, so the `/lib/...` suffix is unquoted and a repo path with a space (common on macOS under `~/Documents/My Projects/`) splits into two words and fails. I'd quote the whole path: `"$FWDIR/lib/apache-rat-${RAT_VERSION}.jar"`, and likewise `mkdir -p "$FWDIR/lib"` and the `--input-exclude-file "$FWDIR/dev/.rat-excludes"` below. ########## table-spec/types/geospatial/cases.json: ########## @@ -0,0 +1,83 @@ +{ + "cases": [ + { + "id": "geometry-crs84", + "valid": true, + "input": "geometry(OGC:CRS84)", + "decoded": {"type": "geometry", "crs": "OGC:CRS84"}, + "clause": "geometry(C) with explicit CRS; the canonical serialized form is unquoted \"geometry(<C>)\"", Review Comment: The clause here says the canonical form is the unquoted `geometry(OGC:CRS84)`, but without a `canonical` field the write direction never gets tested — an implementation that emits `geometry('OGC:CRS84')` or `GEOMETRY(OGC:CRS84)` would pass. Since the clause already pins the spelling, I'd add `canonical` to each explicit case: `"canonical": "geometry(OGC:CRS84)"` here, `"geography(OGC:CRS84, spherical)"` on the geography ones, and so on. Same applies to `geometry-srid` and the four geography algorithm cases below. moomindani's consumer-side run makes this concrete: PyIceberg's regex requires a quoted CRS, so it rejects `geometry(srid:4326)` and writes `geometry('srid:4326')`, while Java reads the quotes into the CRS — a silent bidirectional divergence this fixture catches once `canonical` pins the write side (they filed apache/iceberg-python#3530). ########## dev/validate-fixtures.py: ########## @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +"""Validate the conformance type fixtures against the JSON Schemas in dev/schema/. + +Each cases.json is validated against the base structural schema and the types schema +(which requires clause and spec_ref); case ids must be globally unique. +""" + +import glob +import json +import os +import sys + +import jsonschema + +HERE = os.path.dirname(os.path.abspath(__file__)) +SCHEMA_DIR = os.path.join(HERE, "schema") + + +def _load_schema(name): + with open(os.path.join(SCHEMA_DIR, name), encoding="utf-8") as fh: + return json.load(fh) + + +def _rel(path): + return path.replace(os.sep, "/") + + +def _is_types_surface(path): + p = _rel(path) + return "/table-spec/types/" in p or p.startswith("table-spec/types/") + + +def main(): + root = sys.argv[1] if len(sys.argv) > 1 else "." + base_validator = jsonschema.Draft202012Validator(_load_schema("cases.base.schema.json")) + types_validator = jsonschema.Draft202012Validator(_load_schema("cases.types.schema.json")) + + files = sorted(glob.glob(f"{root}/table-spec/**/cases.json", recursive=True)) + if not files: + print("no cases.json files found", file=sys.stderr) + return 1 + + errors = [] + seen_ids = {} Review Comment: `seen_ids` is global across every `cases.json`, so IDs are unique repo-wide. That's fine today, but the moment a schema or partition-spec surface lands, contributors will reach for `int`, `string`, `timestamp` — names already taken here — and hit a collision that forces artificial mangling. I'd scope uniqueness per surface directory (key on `(surface_dir, id)`); if global is intentional, I'd at least document it where contributors will look. wdyt? ########## table-spec/types/geospatial/cases.json: ########## @@ -0,0 +1,83 @@ +{ + "cases": [ + { + "id": "geometry-crs84", + "valid": true, + "input": "geometry(OGC:CRS84)", + "decoded": {"type": "geometry", "crs": "OGC:CRS84"}, + "clause": "geometry(C) with explicit CRS; the canonical serialized form is unquoted \"geometry(<C>)\"", + "spec_ref": "format/spec.md#appendix-c-json-serialization" + }, + { + "id": "geometry-srid", + "valid": true, + "input": "geometry(srid:4326)", + "decoded": {"type": "geometry", "crs": "srid:4326"}, + "clause": "geometry(C) example from Appendix C is the unquoted \"geometry(srid:4326)\"", + "spec_ref": "format/spec.md#appendix-c-json-serialization" + }, + { + "id": "geometry-default-crs", + "valid": true, + "input": "geometry", + "decoded": {"type": "geometry", "crs": "OGC:CRS84"}, Review Comment: For the default cases it's less clear-cut, so I'll pose it as a question. When the CRS is the default `OGC:CRS84`, should re-serializing emit `geometry` (eliding the default, matching the input) or `geometry(OGC:CRS84)` (always explicit)? Both are plausible and implementations will fork here. Whichever way Appendix C pins it, I'd add a `canonical` so the choice is nailed down — this is the most common geometry value, so an untested write direction here is where interop actually breaks. Same question for `geography-default`. wdyt? ########## table-spec/types/variant/cases.json: ########## @@ -0,0 +1,5 @@ +{ + "cases": [ + { "id": "variant", "valid": true, "input": "variant", "decoded": { "type": "variant" }, "canonical": "variant", "clause": "Semi-structured Types: variant added in v3; Appendix C canonical string", "spec_ref": "format/spec.md#appendix-c-json-serialization" } Review Comment: variant only has the happy path — I'd add at least one `valid: false` case so the rejection direction is covered, e.g. `variant(x)` (variant takes no parameters). Without it, an implementation that accepts any string starting with `variant` passes. ########## dev/requirements.txt: ########## @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Dependencies for the dev/ tooling (fixture validation). +jsonschema>=4.18 Review Comment: I'd pin an upper bound here — `jsonschema>=4.18,<5`. This validator is the conformance gate every future contribution runs through, so an unbounded range means a 5.0 that changes how `iter_errors` surfaces nested errors could silently drop or duplicate violations with no code change on our side. Two of the passes flagged this. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
