Repository navigation
Releases: oasdiff/oasdiff
Release list
v1.35.0-rc.1
Changelog
- a4ba100 Merge pull request #1305 from BogdanYarotsky/property-removal-sunset
- 6eb4a1c checker: removing a deprecated property follows its sunset date
- b478736 Merge pull request #1340 from huguesducharme/security/pin-gha-tags
- 5fc974a security: pin third-party actions and limit workflow token permissions
- 6e25854 Merge pull request #1354 from oasdiff/fix/one-exit-code-for-refused-external-refs
- e4bad04 Merge branch 'main' into fix/one-exit-code-for-refused-external-refs
- 35c5713 Merge pull request #1355 from oasdiff/ci/no-duplicate-go-cache
- 5891476 Merge branch 'main' into ci/no-duplicate-go-cache
- 59a21e9 ci: stop caching the Go module and build dirs twice
- 58d84d5 Merge branch 'main' into fix/one-exit-code-for-refused-external-refs
- 2b95cc9 Merge pull request #1353 from oasdiff/test/external-refs-case-filenames
- 67a867d Merge branch 'main' into fix/one-exit-code-for-refused-external-refs
- ddc0310 Merge branch 'main' into test/external-refs-case-filenames
- b5e7c8b Merge pull request #1351 from oasdiff/chore/external-refs-reader-guard
- a78ebd3 Merge branch 'main' into fix/one-exit-code-for-refused-external-refs
- 5e8f69c Merge branch 'main' into test/external-refs-case-filenames
- b235d92 load: use errors.AsType, and pin the policy on encoded refs
- b7f8295 Merge branch 'main' into chore/external-refs-reader-guard
- e95dcae Merge pull request #1352 from oasdiff/fix/capture-policy-fail-closed
- 15bad32 load: one exit code for a refused external $ref
- 01c7586 test: give each external-refs case its own spec file
- 7f8d1f1 load: rename refPolicy.allow to checkLocation
- 785cdab test: state what the zero refPolicy does, not what it used to
- 93b29ac load: document the shared mechanics on withRecordingReader
- 4ce53b9 load: move refPolicy to its own file
- f2b1c06 load: make the capture reader's ref policy fail closed
- 2d3583a test: cover the external-refs policy on the git and glob capture paths
- 424b330 Merge pull request #1349 from oasdiff/docs/external-refs-page
- c4b24f7 docs: name the external-$ref page for what it covers
- d4a1873 Merge pull request #1346 from oasdiff/docs/security-policy
- 4716d39 Merge branch 'main' into docs/security-policy
- 4acb376 Merge pull request #1347 from oasdiff/fix/external-refs-capture
- 5331dec Apply the external-refs policy in the capture reader
- d06edf2 Add a security policy and point it at private reporting
- 30ddbcb Merge pull request #1328 from oasdiff/fix/1327-security-scheme-urls
- fa9964c checker: lowercase the whole host and trim a default port
- 081de29 test: keep the OAuth URLs in openapi-test3 unchanged from openapi-test1
- e56c382 Merge branch 'main' into fix/1327-security-scheme-urls
- 27811fd Merge pull request #1344 from oasdiff/fix/callback-cycle
- 72e8eb6 Merge branch 'main' into fix/callback-cycle
- b26c8d6 Merge pull request #1343 from oasdiff/fix/extensible-enum-non-string
- ef47a12 Merge branch 'main' into fix/extensible-enum-non-string
- 8b0e4e4 docs: say why extensionValueText falls back to fmt.Sprint
- 465ea60 fix: show non-string extension values as JSON
- 5993557 fix: diff no longer overflows the stack on a callback cycle
- e14ecb7 Merge branch 'main' into fix/1327-security-scheme-urls
- 7b7bfc0 Merge pull request #1336 from oasdiff/fix/duplicate-endpoint-error
- 8caa32e fix: invalid x-sunset and x-stability-level errors show the value
- 6a88fc5 fix: x-extensible-enum checks no longer crash on values that aren't strings
- da5f722 Merge branch 'main' into fix/duplicate-endpoint-error
- 0165dd2 fix: invalid x-since-date error says what the value is and the format expected
- 3627408 fix: drop the fmt import diff.go no longer uses
- 309dc04 refactor: keep the errors mergedPaths returns together in errors.go
- dcfa926 refactor: the duplicate endpoint error words its own hint
- 6d1b680 refactor: diff decides whether path parameter names tell a duplicate apart
- 3eb6df7 fix: invalid x-since-date error names the path of the operation it found
- b665f28 refactor: name the include-path-params flag once
- a9222d8 refactor: duplicate endpoint error is a type, the flag hint is added by the CLI
- bd54d48 fix: duplicate endpoint error also suggests --include-path-params
- afb673d Merge pull request #1335 from oasdiff/fix/diff-list-order
- 0b3a016 refactor: match normalized paths in sorted order, as Paths.Find does
- 763e182 refactor: read responses by key instead of from a copy
- e9ce3af refactor: assign keysDiff results directly
- 6f6f775 Merge remote-tracking branch 'origin/fix/diff-list-order' into fix/diff-list-order
- d51540d refactor: compare map keys in one sorted helper
- 79901cd Merge branch 'main' into fix/diff-list-order
- 9d397a3 fix: duplicate endpoint error names both paths, the same on every run
- b1199fc fix: diff output in the same order on every run
- be6ad7a Merge pull request #1332 from oasdiff/chore/kin-v0.151.0
- 1fbe452 style: gofmt
- 4167631 chore: update kin-openapi to v0.151.0
- 314afca Merge remote-tracking branch 'origin/main' into fix/1327-security-scheme-urls
- 99de9ce Merge remote-tracking branch 'origin/main' into fix/1327-security-scheme-urls
- 1d738d2 checker: check every OAuth flow, and compare security scheme URLs by meaning
v1.34.1
A security patch, cut from v1.34.0 with this change alone. If you run --open, or use the GitHub Action, upgrade.
CLI changes
--allow-external-refs=falseis now enforced when--openis passed (#1347).--openinstalls a reader that captures the text of every file the loader reads, so the review can be sliced into per-endpoint blocks. kin-openapi checks the external-refs setting only when no custom reader is installed, and hands the policy to the reader when there is one, so the setting had no effect on file, URL and glob sources: a spec with an external$refcould read any local file or reach any URL, and the full bytes of what it read went into the review bundle. Refusing now returns the existingExternalRefError(exit code 123), the same as the git-revision path, and--allow-external-refs=trueis unaffected. Reported by Zachariah Mithani (AploSec). See GHSA-6h5x-f3qr-cp23; GitHub Action users should read GHSA-6h8r-wgc5-q3xv, where the affected combination is the default configuration.
Go package changes
load.NewSpecInfoWithCaptureandload.NewSpecInfoFromGlobWithCaptureapply the external-refs policy before reading. A caller that setIsExternalRefsAllowed=falseand loaded through either of them was not getting that setting honoured; a refused$refnow returns*load.ExternalRefError. No exported signature changes.
v1.34.0
Security checks, one finding per shared-schema change, faster large specs
Security requirements are now compared by the access they grant: securing an anonymous endpoint, replacing a top-level requirement and changing how a security scheme sends credentials are all reported as breaking. A change in a schema that many properties reference is reported once, naming the schema, which cuts the output on large specs by up to 97%. Large specs compare much faster, and several sources of run-to-run differences in the output are gone.
This release includes everything tested in the v1.33.1 release candidates. Thanks @victorrentea for reporting five of the detection gaps fixed here (#1280, #1281, #1282, #1283, #1284), @ttyS15 for the shared-schema traversal fix (#1263), @bram96 for the --flatten-allof report and for testing the release candidate on a full spec (#1279), and @dreaded369 for the validate report (#1304).
CLI changes
Security
- No security requirement means anonymous access (#1290). Securing an operation that had no security requirement, directly or by adding a top-level
securityit inherits, is now reported asapi-security-anonymous-access-removed(error), naming what the endpoint now accepts. Making a secured operation anonymous isapi-security-anonymous-access-added(info), instead of the previousapi-security-removederror. Operations that switch between inheriting the top-levelsecurityand declaring their own, includingsecurity: [], are compared using the security actually in effect. - Top-level security changes are reported on each operation that inherits them (#1326). Replacing or narrowing the top-level
securitylist was always reported at info, sobreakingpassed although every inheriting operation now rejects existing clients. Each inheriting operation now getsapi-security-removed,api-security-added,api-security-scope-addedorapi-security-scope-removedat its declared level, with a comment saying the endpoint inherits the API's top-level security requirements. Theapi-global-security-*findings now appear only when no operation inherits the list, at info. These findings have new ids and paths, so their fingerprints change. - Security scheme fields that tell a client how to send credentials (#1288). New checks, all errors:
api-security-component-api-key-name-changed,api-security-component-api-key-in-changed,api-security-component-http-scheme-changed(compared case-insensitively) andapi-security-component-openid-connect-url-changed.api-security-component-bearer-format-changedis info, sincebearerFormatis a hint. When the scheme'stypechanges, only the type change is reported. - Level change:
api-security-component-type-changedis now an error (#1288), so changing a security scheme's type failsoasdiff breaking. Use--severity-levelsto set it back to info. - apiKey header names are case-insensitive (#1288). Renaming an
apiKeyscheme sent in a header fromX-API-Keytox-api-keyis no longer reported, matching other header names.--case-insensitive-headers=falseturns this off.
Shared schemas
- A change in a shared schema is reported once per check (#1273). A change in a schema that several properties of a payload reference was reported at some of them and silently left out at others, depending on how they nested. It is now reported once, at one of those properties, naming the schema, listing the first three properties it applies to and counting all of them, for example
(shared schema: Shared, also at third/shared/id, fourth/shared/id and 1 more). On the Microsoft Graph description, 713,875 findings between two releases become 18,953. - New
sharedSchemafield in JSON and YAML output (#1273):name,propertiesandcount, orcyclicwhen a cycle makes the count unbounded. - What this means for stored results (#1273). The findings for the same change at the other properties are no longer reported, so their fingerprints disappear and review decisions stored against them don't carry forward. The finding that is kept keeps its fingerprint, but its text now ends with the shared schema detail, so an
--err-ignoreor--warn-ignoreline copied from the old text no longer matches it.
Detection rules
- readOnly and writeOnly flips (#1293, fixes #1282). A required response property becoming
writeOnlyis now an error, the same as removing it. In OpenAPI 3.0, a required request property that stops beingreadOnlyis an error, because 3.0 appliedrequiredto it in responses only; in 3.1 and later it is reported as the newrequest-required-property-became-not-read-only-in-openapi-31at info, since clients already had to send it. A request property becomingreadOnlyis now a warning, with a comment saying the spec lets the server either ignore or reject it. - Removing or adding a whole enum is one change (#1289, fixes #1280). Dropping the
enumkeyword was reported as each value removed, which inverted the verdict: it is now info on requests (request-property-became-not-enumand the parameter and body variants) and an error on responses (response-property-became-not-enum,response-mediatype-became-not-enum), since the server may now return values the old enum excluded. Adding an enum to an unconstrained response property isresponse-property-became-enum(info) instead of one error per value. - Enum changes on a body (#1289). Adding a value to a response body enum is now reported as
response-mediatype-enum-value-added(error); it was not reported at all. Adding an enum to a response body (response-mediatype-became-enum) and adding a value to a request body enum (request-body-enum-value-added) are reported at info. dateortimetodate-timeis breaking (#1285, fixes #1281). It was treated as a widening, but a validdateis not a validdate-time, so on a request the change rejects every value that was valid before. It is now reported as a format change.- Unwrapping a nullable header parameter reports one change (#1275). Removing a
oneOf: [{type: "null"}, X]wrapper from a header parameter, or a property of one, also reportednew-required-request-header-property,request-header-property-became-requiredandrequest-header-property-became-enum. Onlyrequest-parameter-became-not-nullableis reported now, as for query parameters.
Large specs and stable output
- Faster on large specs (#1291, #1263).
breakingon the Microsoft Graph description went from 152 to 24 seconds. A chain of shared schemas, each referenced twice, no longer makesbreakingslow down exponentially with its depth (#1263). --flatten-allofgives the same result on every run (#1287, fixes #1279). AnallOfwhose subschemas had their ownallOfcould be merged in an order that changed from run to run, so two identical copies of a spec were sometimes reported as different.- Output order no longer changes between runs (#1272, #1292, #1302). Changes that differ only in media type, the types named by a list-of-types change, and added or removed discriminator mapping keys, OAuth scopes and media type parameters now come out in a fixed order. For list-of-types findings this also makes the fingerprint stable (#1292).
Misc
validatekeeps the same defect in different components apart in a JSON spec (#1312, fixes #1304). A JSON spec has no line numbers, so the same defect in several components was reported once. Each component now gets its own finding, naming the component. The fingerprints of components-rooted findings change, for YAML specs too.oasdiff diffshows an operation declaring or dropping its own security list (#1290), aslistAdded/listDeleted, sosecurity: []appearing or disappearing is no longer invisible.- The Docker image is built with Go 1.27 (#1330), like the release binaries. Go toolchain updated to 1.27.2.
Go package changes
- Breaking: location helpers moved to
checker/location(#1267).checker.NewSource,checker.ParameterSources,checker.SchemaFieldSourcesand the other exported location helpers are now inchecker/location.checker.Sourceremains as an alias. - Breaking: the schema walker moved to
checker/schemawalk(#1269), withWalker.Walk,ModifiedProperties,AddedProperties,DeletedPropertiesandPropertyFullName. - Breaking:
ApiChange.WithSchematakes the root schema and property path as well as the node (#1273).ApiChange.GetSharedSchemareturns the new shared schema detail. - Schema diffs name the component they compare (#1264, #1270).
SchemaDiff.BaseComponentandRevisionComponenthold thecomponents.schemasname, including for a$refwith a description override. They are not part of the diff output. - The diff carries checker context (#1290, #1293): the top-level security lists, the operations present in both specs and each spec's
openapiversion, none of them in the output.CheckBackwardCompatibilityfilters this context on a copy, so the caller's diff is not modified.
v1.33.1-rc.2
Changelog
- e18d4c4 Merge pull request #1273 from oasdiff/feat/consolidate-package
- 7dfbe03 checker: list the other properties when a request property becomes required
- 43f9c42 Merge branch 'main' into feat/consolidate-package
- 19ee026 Merge pull request #1291 from oasdiff/perf/graph-spec
- 02115a0 diff: remove two whole-collection scans per lookup
- 0d227bc schemawalk: pass yield down in pathsAt instead of nesting iterators
- 93ed9cc schemawalk: name shared schemas in sharedSchemaName
- 7004b30 checker: drop the shared-schema comment
- 66de287 checker: keep the shared schema accessors with the other ApiChange accessors
- c97f246 Merge branch 'main' into feat/consolidate-package
- 1b97c7c docs: name the spec the shared-schema measurement comes from
- 32f901a docs: the measured payload may be a request body
- 554b441 docs: say why a shared-schema change is reported once
- d43e15e schemawalk: say what References is built from
- 90b75de diff: name a $ref into a component after the component
- 0af1c82 docs: say when a shared schema has no name
- bcc5e3b docs: simplify which shared schema a finding names
- a57f34d docs: shorten the shared schema section and say which schema is named
- 3dd534e checker: report a shared-schema change once, with its first properties and a count
- 1817769 checker: make the two-parents fixture test what its comment says
- a5f0a7d checker: split expandSharedSchemas into one function per step
- 9a821a7 checker: localize the shared schema detail and format its values like arguments
- 4092db0 checker: keep schema diff fields on changes; tests clear them instead
- 6709d49 Merge branch 'main' into feat/consolidate-package
- 55daeeb checker, consolidate: rewrite hard-to-read comments in plain English
- 7936117 Merge branch 'main' into feat/consolidate-package
- c156923 consolidate: report a shared-schema change once per check, as a mode
v1.33.1-rc.1
Changelog
- 6de374d Merge pull request #1287 from oasdiff/fix/1279-flatten-allof-determinism
- 59cab10 flatten: merge an allOf's pending subschemas before the allOf itself
- b399da0 Merge pull request #1278 from oasdiff/docs/fingerprint-base-changes
- 3f0a888 docs: say when a fingerprint changes because the base changed
- 560c25a Merge pull request #1275 from oasdiff/fix/header-property-transition-claims
- c938fc3 checker: let transitions claim header property findings
- 96875ca Merge pull request #1274 from oasdiff/docs/property-deprecation-removal
- e6dfb85 docs: removing a deprecated property is still reported as breaking
- fc0370b Merge pull request #1271 from oasdiff/docs/fingerprint-formula
- 04e2908 Merge branch 'main' into docs/fingerprint-formula
- 7161d4b Merge pull request #1272 from oasdiff/fix/sort-changes-by-details
- 1abc82d Merge branch 'main' into fix/sort-changes-by-details
- 00ae4ee Merge pull request #1270 from oasdiff/fix/component-name-from-ref
- 80ee188 Merge branch 'main' into fix/component-name-from-ref
- 44bf3d8 Merge pull request #1269 from oasdiff/refactor/schemawalk-package
- 11db07f Merge branch 'main' into refactor/schemawalk-package
- f85f867 Merge pull request #1268 from oasdiff/refactor/claims-after-checks
- 6baf9eb checker: sort changes that differ only in media type deterministically
- c85756f docs: the fingerprint hashes the message arguments, not its text
- 78337a0 diff: name a $ref with a description override after its component
- 5a96a3b checker: move the schema walker to checker/schemawalk
- 889d755 checker: decide transition claims after the checks run
- 7699bcb Merge pull request #1267 from oasdiff/refactor/location-package
- 9dd9fda checker: move the location helpers to checker/location
- 378e7bb Merge pull request #1263 from ttyS15/fix/checker-shared-subgraph-walk
- 7658634 Merge branch 'main' into fix/checker-shared-subgraph-walk
- c0b58fa Merge pull request #1264 from oasdiff/feat/schema-diff-component-name
- 485b92a diff: a node is not written twice
- 0a51c6a diff: Empty says what it excludes, not where it is called
- 496100b diff: seed the component names where the diff is built
- 8301e09 diff: drop a comment clause for a case that cannot happen
- 2bf9085 diff: trim the component-name comments
- 4c99c87 Revert "test: pin every path to a shared component"
- a176d57 diff: a schema diff names the component it compares
- cfe52f2 test: pin every path to a shared component
- b5643ec fix(checker): visit shared schema diffs once per context
v1.33.0
Recursive schemas: fast, deterministic, and no longer repeated per path
v1.32.0 could run for hours on a spec whose schemas reference each other in cycles, and v1.32.1 fixed that by returning to v1.31.0's cycle handling, which gave up the deterministic output v1.32.0 had introduced. This release reworks the handling so that both hold at once, on the two specs reported in #1252 and on everything else.
Thanks @simonbility for the report and the specs, @philippnagel for the second spec and for testing the release candidate against it, and @BigMichi1 for the determinism report that started this.
CLI changes
Recursive schemas
- Specs with cyclic schemas compare in about a second, with or without
--flatten-allof(#1254, fixes #1252). The two specs reported there are the measure: one needed--flatten-allofand ran for hours, the other has 134 schemas referencing each other in a single cycle and never finished at all. Both now complete in about a second, including the combination v1.32.1 could not do. - Structured output for a schema in more than one reference cycle is deterministic again (fixes #1230). v1.32.0 fixed this, v1.32.1 had to revert it to get rid of the hang, and this release has both. Identical inputs produce identical bytes.
--flatten-allofkeeps one copy of each schema. It used to copy a schema once for every place that referenced it: one reported spec grew from 5,195 schema objects to 3.5 million in memory. The flattened output is unchanged, and flattening it now takes milliseconds rather than seconds.- A change inside a recursive schema is reported once per entry into the cycle rather than once for every path through it. Every operation that reaches the change still reports it. On one reported spec a single added property produced 241,978 changelog lines before and 9,162 now.
- A cycle edge no longer repeats one level of the diff, and a
$ref-to-inline refactor inside a cycle is recognized as a refactor, the same as one outside a cycle.
Upgrading from v1.32.1
- The
circularReffield is removed fromdiff --format json|yamloutput again, as in v1.32.0: cyclic-reference mismatches appear as ordinary field-level diffs, which say more. v1.32.1 had restored it along with v1.31.0's cycle handling. - Diffs of schemas that take part in more than one reference cycle change once, and then hold steady.
Go package changes
v1.33.0-rc.1
Release candidate: recursive schemas, fast and deterministic
This is a pre-release of the reworked handling of recursive schemas (#1254), published so that users with large or cyclic specs can try it before it becomes v1.33.0. Further release candidates may follow, depending on review and on your feedback. It is not published to Homebrew, and the GitHub Action stays on v1.32.1.
Please test it on your real comparisons and report on #1252, especially any change that v1.32.1 reports and this candidate does not.
Install
go install github.com/oasdiff/oasdiff@v1.33.0-rc.1
docker run --rm -t tufin/oasdiff:v1.33.0-rc.1 --help
Binaries for every platform are attached below.
CLI changes
Recursive schemas
- Specs with cyclic schemas no longer hang, with or without
--flatten-allof(#1254, fixes #1252). The two specs reported on #1252 take about a second each, including one where 134 schemas reference each other in a single cycle, which v1.32.1 can only compare without--flatten-allof. - Structured output for schemas in more than one reference cycle is deterministic again (fixes #1230 again). v1.32.0 fixed this, and v1.32.1 had to revert that fix to get rid of the hang; this candidate has both.
--flatten-allofkeeps one copy of each schema. It used to copy a schema once for every place that referenced it, which on one reported spec grew 5,195 schema objects to 3.5 million in memory. The flattened output is unchanged.- A change inside a recursive schema is reported once per entry into the cycle, rather than once for every path through it. Every operation that reaches the change still reports it; the changelog just repeats it at fewer places (on one reported spec, a single added property went from 241,978 changelog lines to 9,162).
- A cycle edge no longer repeats one level of the diff, and the
circularReffield is removed fromdiffjson/yaml output again, as in v1.32.0: cyclic mismatches appear as ordinary field-level diffs. - A
$ref-to-inline refactor inside a cycle is recognized as a refactor, the same as one outside a cycle.
Go package changes
- Breaking:
diff.SchemaDiff.CircularRefDiffis removed again, as in v1.32.0. flatten/allof.MergeSpecmerges the whole document with one state, so a schema referenced from several places stays one object after the merge.
v1.32.1
Fixes the v1.32.0 hang on cyclic schemas
v1.32.0 can run for hours on a spec whose schemas reference each other in cycles (#1252, two reports within a day). This release reverts the two v1.32.0 changes to cycle handling in the diff, so recursive schemas are handled exactly as in v1.31.0, and keeps everything else from v1.32.0. A rewrite of the cycle handling that fixes the hang and the non-determinism together (#1254) follows as a pre-release.
CLI changes
Cyclic schemas
- The v1.32.0 hang on cyclic schemas is fixed by returning to v1.31.0's cycle handling (#1255, fixes #1252). Both reported specs complete again: one that needed
--flatten-allof, and one with 134 schemas on a single reference cycle that hung with no flag at all. One limit of v1.31.0 comes back with it: that second spec still does not finish with--flatten-allof; #1254 fixes that too. Specs without cycles are unaffected; on a large real-world spec the changelog is identical, line for line, to both v1.31.0's and v1.32.0's. Thanks @mako365-philippnagel for the second spec. - Two things come back with the revert. The
circularReffield is back indiff --format json/yamloutput, exactly as in v1.31.0. And the non-deterministic structured output for a schema that takes part in more than one reference cycle (#1230), which v1.32.0 had fixed, is back until #1254 ships; identical inputs can again produce one of two outputs for such schemas.
checks command
- Look up a single check or location (#1249).
oasdiff checks changelog --id <check-id>shows one check (the full record in json/yaml; the flag shell-completes to the catalog ids, and an unknown id is an error).--location <string>keeps the checks whose claimed location contains the string, on the listing and oncoveragealike. Generated rules are marked as such in the listing. - Subcommands are grouped in
--helpand the README (#1250): compare two specs (breaking,changelog,diff,summary), process a single spec (validate,upgrade,flatten), git integration (breaking-files,git-diff-driver), reference (checks,schema). No behavior change.
Docs
- CHECKS.md documents the guards dimension (#1248): the GUARDS column of
oasdiff checksand its six tags (read-only,write-only,sanctioned,non-success,has-default,negotiated), each defined in contract terms.
Go package changes
v1.32.0
56 new checks, cycle-proof diff and flatten
The generated-rule engine now covers every increase and decrease of every bound keyword (catalog: 699 to 755 checks), and recursive schemas are handled end to end: no more stack overflows in diff or flatten, deterministic structured output, and flattened recursion that always serializes under stable names.
CLI changes
Detection rules
- Every bound keyword's increase and decrease is now checked (#1232). The rule engine that generates set/unset checks now also generates increased/decreased ones, closing 56 gaps at once: response headers get their first increase/decrease checks, parameters gain
maxProperties/minProperties/minContains/maxContainscoverage, and the widening response directions (for exampleresponse-body-max-decreased) are reported at info level.multipleOfis deliberately excluded: numeric order does not decide its effect, so it keeps its dedicated changed/generalized/specialized checks. - Exclusive bounds join the generated rules, fixing three latent bugs (#1226). In the OpenAPI 3.0 boolean form,
exclusiveMaximum: falsetotrueused to report nothing (it is a breaking set), addingexclusiveMaximum: falsewas reported as breaking (it changes nothing, false means not exclusive), and the false-to-true transition could be misreported as a value increase. - Added and deleted properties under an unflattened
allOfcarry the disclaimer (#1229). Existence changes insideallOfbranches now get the same warning cap and--flatten-allofhint as other findings there, and a schema that itself has anallOfis recognized by presence rather than only when theallOfchanged. oasdiff checksshows each rule's guards (#1227). A GUARDS column and a--tagsdimension expose the read-only / write-only guards, so a verdict downgraded by a guard is explained by the catalog instead of looking like a bug.
Recursive schemas: no crashes, deterministic output, serializable flatten
- Diffing ref-less cyclic schemas no longer overflows the stack (#1231, thanks @dimitropoulos). Schemas that reference themselves through in-memory links rather than
$ref(for example after--flatten-allofmerges a recursive component) are cut safely at the point of re-entry, whatever shape the cycle has. oasdiff flattenof recursiveallOfmerges always serializes (#1233, #1240, #1245). A cycle keeps its$refwhere one exists, a set that merges to a single schema reuses that schema with its name, and a genuinely anonymous merged cycle is hoisted intocomponents.schemasunder a name built from the merged component names (AllOfMerged_NodeA_NodeB), so the same cycle keeps the same name in every revision. Documented in docs/ALLOF.md (#1244).- A merge mixing a recursive branch with other constraints keeps them all (#1243). Such a node becomes an
allOfof the named cycle and the merged remaining constraints instead of silently dropping them, which could previously hide breaking changes in the flattened comparison. - Structured output for multi-cycle schemas is deterministic (#1237, thanks @BigMichi1 for the precise report in #1230). A schema participating in more than one reference cycle produced one of two different
--format json/yamloutputs at random; identical inputs now produce identical bytes, and the output is also more complete, each cycle edge carrying its own diff. Diffs of such schemas change once when you upgrade. - The
circularRefoutput field is removed (#1238). Cyclic-reference mismatches now appear as ordinary field-level diffs, which say strictly more; a pure rename of a cyclic$refis no longer reported, matching how non-cyclic$refrenames behave.
Validate
- New lint: mutability flags no request or response can ever see (#1228). A
readOnlyon a schema reachable only from requests, orwriteOnlyonly from responses, can never take effect;oasdiff validatenow flags both, walking webhooks and callbacks too.
Misc
- Ignore and severity-level files no longer swallow read errors (#1234). A failed read, for example a line past the scanner's size limit, is reported instead of silently dropping the unread entries.
- The install script is quoting-safe (#1241).
install.shhandles paths containing spaces, and the repo's shell (including workflowrun:blocks) is now gated by shellcheck and actionlint.
Go package changes
- Breaking:
diff.SchemaDiff.CircularRefDiffis removed (#1238), together with the ref-string circular guard it powered; the in-flight pair guard introduced in #1231 handles cycle termination for every cycle shape. - Breaking:
checker.BoundSetUnsetCheckis renamedBoundCheck(#1232), reflecting that it now reports increase and decrease as well. diff.SchemaBoundgainsWasIncreasedandWasDecreased(#1232), completing the bound classification API alongsideWasSetandWasUnset.checker.GetTransitionsexposes transition metadata (#1225): the named transition recognizers with the change kinds they claim and the checks that report them, powering the public OpenAPI Changes Model export.flatten/allof:MergeSpecguarantees marshalable output for recursive inputs (#1233, #1240, #1243): it repairs shared-reference identity after the in-place merge and names every anchored cycle.Merge(single schema, no components section) documents that a nameless recursive input yields an in-memory cycle that does not marshal.- Go 1.27 (#1224): toolchain moved to Go 1.27 and direct dependencies updated.
v1.31.0
Complete set/unset coverage, and read-only properties reported right
Every ordered constraint keyword (maximum, minimum, multipleOf, maxLength, minLength, maxItems, minItems, maxProperties, minProperties, minContains, maxContains) now has set and unset checks on both sides of the wire, at body, property, parameter, and response-header level: 106 new checks, taking the catalog from 575 to 681, each with its severity derived from the same reviewed model introduced in v1.30.0. And changes to read-only request properties are no longer false breaking errors: oasdiff now knows a readOnly property never appears in a request, reports such changes at info with a comment saying why, and applies the mirror rule to writeOnly properties in responses.
CLI changes
Changes that now fail oasdiff breaking
- Removing a constraint from a response is an error (#1215, #1219). Dropping
maximum: 100from a response property means the server may now return values no old client had to handle, the same widening that removingmaxLengthalready reported. Every keyword now has the unset check, on response bodies, properties, and headers. If a specific case is intended, lower the check with--severity-levels. - Setting a constraint on a request parameter is an error for every keyword (#1219).
multipleOf,minLength,maxItems,maxProperties,minProperties,minContains, andmaxContainsset on a parameter previously reported nothing (or misreported, see below); setting a bound rejects values the previous contract accepted, so they now match the existingmax-setfamily, with the same explanatory comment.
Changes that no longer fail
- Restricting a read-only request property is informational (#1213, closes #1211). A
readOnlyproperty never appears in a request, so adding apattern, setting amaximum, changing its type, or any other request-side restriction cannot invalidate one. Forty-one checks previously reported these as errors; they now report info with the comment "The property is read-only, so it never appears in requests and this change cannot invalidate one." The mirror applies towriteOnlyproperties in responses. An explicit--severity-levelsentry for a check keeps whatever level you set. The breaking changes documentation describes both this rule and the existing allOf case. - Previously dropped read-only changes are visible again (#1217, closes #1189). Twenty-five checks used to skip read-only request properties (and write-only response properties) entirely, reporting nothing at all; those changes now appear at info with the same comment.
Misreport fixes
- Setting a lower bound where none existed no longer reports as an increase (#1215, #1219). For
minLengthandminPropertieson bodies and properties, andminLengthandminItemson parameters, going from absent to a value reported as "increased from 0" and removing the bound as "decreased to 0". These now report as set and unset, the same fixminItemsreceived in v1.30.
New checks not listed above
- The remaining set/unset cells at info level: unsetting a request constraint (the request accepts more, visible in the changelog for the first time) and setting a response constraint (the response promises more). All 106 new checks appear in
oasdiff checks changelogand in the coverage listing.
Go package changes
- The severity model is callable (#1212).
rules.DeriveLevel(effect, direction, guards...)is the severity law as a function, andRule.DerivedLevel()applies it to a rule's own metadata. Every rule's registered level equals its derived level, enforced in CI. diff.SchemaBoundslists the ordered constraint keywords (#1215, #1219). Each entry names the keyword, where its diff lives, and how that diff encodes absence (nil for pointer-backed fields, 0 for the plain-uint64 lower bounds), withWasSetandWasUnsetclassification methods. The checker's generated rules and any library caller share one definition of "this bound appeared or disappeared".checker.GetAllRulesincludes generated rules (#1215). The set/unset rules are generated from a keyword table rather than written by hand; they carry ids, levels, and locations like any other rule, so callers that enumerate rules see them transparently.
