Skip to content

Releases: oasdiff/oasdiff

v1.33.0

Choose a tag to compare

@reuvenharrison reuvenharrison released this 01 Oct 16:27
322d7ae

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-allof and 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-allof keeps 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 circularRef field is removed from diff --format json|yaml output 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

  • Breaking: diff.SchemaDiff.CircularRefDiff is removed again (#1254), together with the ref-string circular guard, as in v1.32.0. v1.32.1 had restored both.
  • flatten/allof.MergeSpec merges a document with one state (#1254), so a schema referenced from several places stays one object after the merge.

v1.33.0-rc.1

v1.33.0-rc.1 Pre-release
Pre-release

Choose a tag to compare

@reuvenharrison reuvenharrison released this 15 Sep 19:10
b72eed9

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-allof keeps 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 circularRef field is removed from diff json/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.CircularRefDiff is removed again, as in v1.32.0.
  • flatten/allof.MergeSpec merges the whole document with one state, so a schema referenced from several places stays one object after the merge.

v1.32.1

Choose a tag to compare

@reuvenharrison reuvenharrison released this 15 Sep 18:42
a47e8af

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 circularRef field is back in diff --format json / yaml output, 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 on coverage alike. Generated rules are marked as such in the listing.
  • Subcommands are grouped in --help and 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 checks and its six tags (read-only, write-only, sanctioned, non-success, has-default, negotiated), each defined in contract terms.

Go package changes

  • diff.SchemaDiff.CircularRefDiff is back (#1255), with the ref-string circular guard that powers it, as in v1.31.0. Code written against v1.32.0 that relied on the field being absent sees it again; it is removed once more by #1254.

v1.32.0

Choose a tag to compare

@reuvenharrison reuvenharrison released this 13 Sep 14:49
560d26e

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/maxContains coverage, and the widening response directions (for example response-body-max-decreased) are reported at info level. multipleOf is 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: false to true used to report nothing (it is a breaking set), adding exclusiveMaximum: false was 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 allOf carry the disclaimer (#1229). Existence changes inside allOf branches now get the same warning cap and --flatten-allof hint as other findings there, and a schema that itself has an allOf is recognized by presence rather than only when the allOf changed.
  • oasdiff checks shows each rule's guards (#1227). A GUARDS column and a --tags dimension 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-allof merges a recursive component) are cut safely at the point of re-entry, whatever shape the cycle has.
  • oasdiff flatten of recursive allOf merges always serializes (#1233, #1240, #1245). A cycle keeps its $ref where one exists, a set that merges to a single schema reuses that schema with its name, and a genuinely anonymous merged cycle is hoisted into components.schemas under 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 allOf of 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/yaml outputs 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 circularRef output field is removed (#1238). Cyclic-reference mismatches now appear as ordinary field-level diffs, which say strictly more; a pure rename of a cyclic $ref is no longer reported, matching how non-cyclic $ref renames behave.

Validate

  • New lint: mutability flags no request or response can ever see (#1228). A readOnly on a schema reachable only from requests, or writeOnly only from responses, can never take effect; oasdiff validate now 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.sh handles paths containing spaces, and the repo's shell (including workflow run: blocks) is now gated by shellcheck and actionlint.

Go package changes

  • Breaking: diff.SchemaDiff.CircularRefDiff is 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.BoundSetUnsetCheck is renamed BoundCheck (#1232), reflecting that it now reports increase and decrease as well.
  • diff.SchemaBound gains WasIncreased and WasDecreased (#1232), completing the bound classification API alongside WasSet and WasUnset.
  • checker.GetTransitions exposes 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: MergeSpec guarantees 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

Choose a tag to compare

@reuvenharrison reuvenharrison released this 05 Sep 07:19
921b70f

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: 100 from a response property means the server may now return values no old client had to handle, the same widening that removing maxLength already 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, and maxContains set on a parameter previously reported nothing (or misreported, see below); setting a bound rejects values the previous contract accepted, so they now match the existing max-set family, with the same explanatory comment.

Changes that no longer fail

  • Restricting a read-only request property is informational (#1213, closes #1211). A readOnly property never appears in a request, so adding a pattern, setting a maximum, 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 to writeOnly properties in responses. An explicit --severity-levels entry 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 minLength and minProperties on bodies and properties, and minLength and minItems on 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 fix minItems received 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 changelog and 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, and Rule.DerivedLevel() applies it to a rule's own metadata. Every rule's registered level equals its derived level, enforced in CI.
  • diff.SchemaBounds lists 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), with WasSet and WasUnset classification methods. The checker's generated rules and any library caller share one definition of "this bound appeared or disappeared".
  • checker.GetAllRules includes 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.

v1.30.0

Choose a tag to compare

@reuvenharrison reuvenharrison released this 30 Aug 22:12
9e66f6f

Provable check coverage, derived severities, boolean schemas, positional prefixItems

The largest change to breaking-change detection since the checker was introduced: every check's severity is now derived from a single reviewed model of what each change does to the API contract, enforced in CI, with zero recorded exceptions. That audit produced 58 new checks and corrected the severity of 36 existing ones, so some changes that previously passed oasdiff breaking with a warning now fail it, and some that failed now pass. Teams that disagree with any individual verdict can override it with --severity-levels. The release also brings OpenAPI 3.1 boolean schemas (items: false tuples), positional prefixItems comparison, OpenAPI 3.2 QUERY and custom methods, and a new breaking-files command for pre-commit hooks.

CLI changes

Severity corrections: changes that now fail oasdiff breaking

  • Setting a bound where none existed is an error, not a warning (#1174). All 18 request-*-{max,max-length,min,min-items,exclusive-max,exclusive-min}-set checks: adding a bound rejects values the previous contract accepted, the same shape as became-enum, const-added and pattern-added, which were already errors. If your clients are known to stay within the new limit, lower the check with --severity-levels.
  • Security requirement and scope changes are errors, not info (#1174). api-security-removed, api-global-security-removed, api-security-scope-added, api-global-security-scope-added: requirements are alternatives, so removing one strands clients authenticating with it, and scopes are conjunctive, so adding one rejects tokens that lack it.
  • Adding a branch or value to a response union is an error (#1174). response-body-any-of-added and response-property-any-of-added now match response-body-one-of-added, and response-property-enum-value-added is an error: the server may return a value no client was written to handle (use x-extensible-enum for value sets that are meant to grow). response-property-pattern-removed is an error and response-property-pattern-changed a warning for the same reason.

Severity corrections: changes that no longer fail

  • Request parameter defaults are informational (#1174). request-parameter-default-value-{added,changed,removed} drop from error to info, matching the body and property default checks: a default is a server-side fallback and does not change which requests are valid.
  • Removing an optional response element is informational (#1174). response-optional-property-removed and optional-response-header-removed: a conforming client already tolerates the element's absence. request-body-all-of-removed, request-property-all-of-removed and response-required-property-became-not-write-only drop to info on similar reasoning, and response-media-type-name-changed becomes a warning with a comment naming what cannot be determined.
  • oneOf wrapping splits on whether the original schema survives (#1174). Wrapping a body in a oneOf that keeps the original schema as one of the alternatives is now the new request-body-wrapped-in-one-of-original-preserved / response-body-wrapped-in-one-of-original-preserved at warning level, with a comment explaining the residual risk; a wrapping where no alternative accepts what the original did keeps the error.

New checks

  • 44 constraint-keyword checks (#1174). maxItems, maxProperties, minProperties, multipleOf and uniqueItems were not checked at all; they now are, across request body, property and parameter and the response side, in both directions, including read-only variants and the multipleOf generalized/specialized cases where one bound divides the other. The multipleOf comparison is exact rational arithmetic, so 0.3 to 0.1 is a generalization and a ratio merely close to an integer is not (#1205).
  • 14 boolean-schema checks (#1191, #1203). See below.

Boolean schemas (OpenAPI 3.1)

  • true and false schemas load and diff (#1184, #1191). A schema written as a JSON Schema boolean failed to parse before; it now loads, and the diff models it. A schema becoming false accepts nothing: the new *-schema-became-false checks report it as an error on the request side and for a response body (the client's media type can no longer be inhabited), across body, property, parameter, parameter property and response header. The reverse transitions report as *-schema-became-not-false. A change between {} and true is a document edit with no contract effect: visible in oasdiff diff, silent in the changelog.
  • Closing a tuple with items: false is detected (#1203). Adding items: false to a schema that had no items invalidates every array longer than the prefix and previously reported nothing.
  • A schema arriving as false or true is classified, not just counted (#1191). A media-type schema added as false reports as schema-became-false instead of an informational "schema added", and one added as true or {} (which accepts exactly what the absent schema accepted) reports nothing instead of a spurious error on the request side.

prefixItems is positional

  • Reordering entries is detected (#1192). prefixItems entries were matched as an unordered set, so swapping [string, integer] to [integer, string] reported no change when every position now validates against a different schema. Entries are now paired by index, so a reorder or in-place edit reports as a located type change (prefixItems[subschema #1]), and oasdiff diff shows per-position modifications.
  • prefixItems verdicts follow the items schema (#1200). An entry that restates the items schema it displaces changes nothing and now reports nothing; otherwise the eight added/removed checks report a warning with a comment explaining why the direction cannot be determined: an added entry constrains an open position when items is absent but lengthens the tuple when items is false, so the old fixed verdicts (info one way, error the other) asserted a direction the spec does not fix.

OpenAPI 3.2

  • QUERY and custom methods are compared (#1135). Operations under the 3.2 query field and additionalOperations map were invisible to the diff (parked in extensions, then skipped by the fixed method list); they are now diffed like any other operation.
  • New validate rules for 3.2 and boolean schemas (#1184). additional-operations-*, query-field-for-3-2-plus, boolean-schema-for-3-1-plus and boolean-schema-with-other-keywords join oasdiff validate.

New commands

  • oasdiff breaking-files checks each changed spec against a git ref (#1183, thanks @ChrisJr404): one comparison per spec against the same path in the ref, an aggregated exit code, and a skip with a fetch hint for specs not in the base. Designed for pre-commit hooks; an example .pre-commit-config.yaml ships in examples/.
  • oasdiff checks changelog coverage shows what the checker covers (#1174). Every possible edit to an OpenAPI document, derived mechanically from the object model (5,564 locations, 15,255 edits), with the checks that cover it or the reason it is waived. --tags filters by direction, area, kind, action and status; --patterns lists the claim patterns. Uncovered edits and stale waivers fail the build, so the coverage listing is enforced, not aspirational.

Misc

  • oasdiff validate findings always carry a line and column (#1177). duplicate-required-field and duplicate-tag reported a file with no location.
  • An unsupported --template format is rejected before the specs load (#1178), like the equivalent --color mismatch, instead of after the full diff run.
  • Localization is complete and enforced (#1202). Six response-header checks had no description in any language and one message was missing in Russian; a test now fails on any check id without a message and description in every locale.

Go package changes

  • Rule metadata is public: checker/rules (#1174). Every rule declares its Direction, Area, Kind, Effect (widens, narrows, incomparable, unknown, none, violation) and Guards, and checker.BackwardCompatibilityRule carries them. Severity is derived from that metadata by a law enforced in tests: narrowing a request or widening a response is an error, the reverse is info, undecidable is a warning. checker/metaschema (the edit-space model) and checker/coverage (the audit) are new packages.
  • diff understands boolean schemas (#1191). SchemaDiff.AlwaysDiff carries transitions of the JSON Schema boolean form, and SchemaRefsValidationEquivalent treats true as the empty schema while keeping false distinct.
  • diff pairs prefixItems by index (#1192). SchemaDiff.PrefixItemsDiff reports positional modifications instead of set-matched additions and deletions; consumers of the diff JSON see modified entries where reorders previously produced nothing. PrefixItemsValidationEquivalent reports whether two schemas validate every prefix-covered position identically (#1200), and OneOfWrappingDiff.OriginalPreserved reports whether a oneOf wrapping keeps the base schema as an alternative (#1174).
  • Color primitives moved to a colorize package (#1174). checker keeps type and constant aliases, so existing callers compile unchanged.
  • Breaking: the unused checker/generator package is removed (#1174).

Misc

  • Builds lift to Go 1.26.7 (#1202). The toolchain directive picks up standard-library security fixes (crypto/tls, net/url, html/template, encoding/asn1) for release binaries, go install builds and local builds alike; golang.org/x/text moves past CVE-2026-56852.

v1.29.1

Choose a tag to compare

@reuvenharrison reuvenharrison released this 16 Aug 11:59
2bb87ba

--flatten-allof now flattens what your diff actually reads

A single-fix release: schemas reached through a $ref (the most common shape in real specs) were escaping allOf flattening entirely, so --flatten-allof often had no effect. Anyone using the flag with oasdiff breaking or changelog (via the CLI, GitHub Action, or Docker image) will see the flag start doing its job on these specs, with sharper verdicts and cleaner property paths.

CLI changes

--flatten-allof

  • allOf is now merged where a schema is used, not only where it is defined (#1154). A $ref is a separate reference sharing the definition's schema value, and the merger previously repointed only the definition's reference, leaving every use of the schema (for example schema: { $ref: '#/components/schemas/Pet' } under a response) reading the unmerged original. Since the diff traverses schemas through their uses, an allOf reached under a $ref survived --flatten-allof completely, and oasdiff breaking --flatten-allof could produce output identical to running without the flag. The merged content is now written into the schema every $ref already points at, so flattening takes effect at the point of use. Serialized output of oasdiff flatten was already correct and is unchanged.
  • Expected output changes when the fix kicks in (#1154). On specs that hit this bug, property paths lose their allOf[...] prefix (the branch is no longer part of the path), verdicts sharpen where a sibling allOf branch previously hid a change, and changes under such an allOf stop carrying the unmerged-allOf disclaimer, which had been advising users to pass a flag they had already passed. All three are the intended behaviour of the flag, now applied consistently.

v1.29.0

Choose a tag to compare

@reuvenharrison reuvenharrison released this 16 Aug 10:28
01023a6

OpenAPI 3.2 streamed bodies, honest verdicts under unflattened allOf, sharper severities

oasdiff now checks the contract of streamed bodies (SSE, JSON Lines) via OpenAPI 3.2's itemSchema, tells you when a verdict rests on an allOf it could not compare exactly (and caps it at warning), reports a removed response schema as the error it always was, and stops the allOf flattener from dropping sixteen OpenAPI 3.1 keywords.

CLI changes

OpenAPI 3.2 streamed bodies (itemSchema)

  • Changes to streamed item schemas are now detected (#1139). An OpenAPI 3.2 media type can carry an itemSchema typing each item of a streamed body (an SSE event, a JSON Lines record); oasdiff previously ignored it, so a breaking change to a streamed item's contract came back clean. All existing body checks now run against the item schema as well as the body schema, with an (item schema) marker on the message so you can tell which one changed. Five new change IDs cover an item schema appearing or disappearing: request-body-media-type-item-schema-added, response-body-media-type-item-schema-removed, and response-body-media-type-item-schema-removed-untyped are errors; the reverse directions are info. docs/OPENAPI-31.md gained a 3.2 section.
  • Enum and property-stability checks cover streamed items too (#1143, #1151). These checks were moved onto the shared media-type traversal, which also fixed a real output gap: enum changes on a body with multiple media types now say which media type they belong to, e.g. request body enum value removed 'b' (media type: application/json), instead of printing two indistinguishable lines.

Disclaimers: saying what the comparison could not see (#1147)

  • Changes inside an unflattened allOf are capped at warning, with an explanation. Without --flatten-allof, oasdiff compares allOf branches one by one, so it cannot tell whether a sibling branch still provides what one branch dropped. Such verdicts previously came out as flat errors that could vanish (or appear) when the flag was added. They are now reported at most as warnings, with an appended note naming the uncertainty and the flag that resolves it, and a disclaimers array (["all-of-not-flattened"]) in json and yaml output. A level you set explicitly via --severity-levels still wins over the cap.
  • request-body-all-of-added now reports warning instead of error as a consequence: on its own fixture, --flatten-allof reports nothing, so the unconditional error was a false positive.
  • The required-version-bump check follows the reported level. It previously read each rule's declared level, so a change downgraded to warning by a disclaimer still demanded a major version bump. It now reads the level actually reported for the change.

Severity correction

  • Removing a response schema is now an error (#1142). response-body-media-type-schema-removed was a warning while both the change that contains it (removing the media type) and the change it contains (removing one required property) were errors. A media type with no schema places no constraint on the body at all, so every guarantee the consumer had is gone. oasdiff breaking reports the same set of changes as before, but pipelines gating on --fail-on ERR that tolerated this will now fail, which is the point of the change.

Flatten

  • Sixteen OpenAPI 3.1 keywords are no longer dropped by the allOf flattener (#1145). unevaluatedProperties, if/then/else, dependentSchemas, and thirteen more JSON Schema 2020-12 keywords on the outer schema were silently lost from every merge, including a schema with no allOf at all, so a closed schema became open and whole validation branches disappeared from --flatten-allof comparisons. They now pass through, and oasdiff diff between a spec and its flattened output no longer reports the loss. A keyword on an allOf subschema is still not merged; docs/ALLOF.md documents that remaining limitation.

Misc

  • Encrypted review uploads identify the client software (#1138). The upload's User-Agent is now oasdiff-cli/<version> (plus the CI platform when one is declared, e.g. github-actions) instead of an unversioned oasdiff-cli. Nothing identifying is added; this names the client, not the user.

Go package changes

Disclaimers surface

  • Breaking: the checker.Change interface gained GetDisclaimers() []Disclaimer (#1147). Any external implementation of Change must add the method (returning nil is fine; ComponentChange and SecurityChange do exactly that). ApiChange carries a new Disclaimers field and a WithDisclaimers method, which is additive and de-duplicating, and the new checker.Disclaimer type serializes by name (all-of-not-flattened) in json and yaml.

Misc

  • New diff.MediaTypeDiff.ItemSchemaDiff field (#1139). A *SchemaDiff for the OpenAPI 3.2 itemSchema, serialized as itemSchema in diff output, alongside the existing SchemaDiff for the whole-body schema.

v1.28.0

Choose a tag to compare

@reuvenharrison reuvenharrison released this 06 Aug 12:48
f7318d9

Lower memory through a diff

A single dependency bump, to kin-openapi v0.146.0, which cuts the memory a diff holds for its whole run by about a third on large specs. No CLI or behaviour changes.

CLI changes

Memory

  • A diff holds roughly a third less memory (#1134). oasdiff loads both specs with source origins enabled, and both documents stay resident for the length of the operation, so the memory the origins occupy is a floor on the whole run rather than a spike during load. Two changes in kin-openapi v0.146.0 lower that floor: Origin.Fields became a slice, and a document's origin tree is now retained only when a $ref can actually reach into it untyped.

    Measured by loading two APIs.guru specs (11.4 MB and 10.3 MB) with origins on and reading the heap after a forced GC:

    retained heap total allocated
    v1.27.0 262 MB 2042 MB
    v1.28.0 179 MB 1959 MB

    Peak RSS is not meaningfully different. The saving is in what stays held, which is what matters to a long-running service holding parsed specs and to a memory-capped container, rather than to the high-water mark of a short CLI run.

Go package changes

Breaking: Origin.Fields is a slice

  • openapi3.Origin.Fields is now FieldLocations, not map[string]Location (#1134). This comes from kin-openapi v0.146.0 and reaches you through oasdiff's dependency on it. A Go caller that reads a field location by name changes from an index to a lookup:

    // before
    loc, ok := origin.Fields[name]
    // after
    loc, ok := origin.Fields.Lookup(name)

    Lookup returns the same (Location, bool) pair, so the surrounding code is unchanged. Ranging over Fields and taking its length still work. Each Location carries its own Name, which is what the lookup matches on.

    JSON and YAML output are unaffected: FieldLocations marshals as the same name-keyed object the map produced, so anything consuming oasdiff diff -f json sees no difference.

v1.27.0

Choose a tag to compare

@reuvenharrison reuvenharrison released this 30 Jul 19:33
fb8babb

This release adds a versioning policy, more validate lints, and reorganizes oasdiff checks into one listing per rule set.

CLI changes

Versioning policy

  • A breaking change now reports the version bump it did not get. If you version your API with semver, oasdiff compares info.version on each side against the severity of the changes it found, and reports a breaking change that shipped without a major version increase. There are three ids, one per way to violate the policy: api-version-not-bumped (the version is unchanged), api-version-decreased (it moved backwards), and api-major-version-not-bumped (it moved, but not the major). All default to INFO, so nothing fails on their own; raise them to err with --severity-levels to enforce, or set them to none to switch them off (#1133, closing #1007, from a request by @rethab in oasdiff-action#154). Nothing is reported unless a breaking change is present and both versions parse as semver, so specs versioned by date, by a bare v1, or not at all are left alone. Below 1.0.0 a minor bump satisfies the policy, since semver gives the minor the major's role there. See VERSIONING.md.

oasdiff checks now names its rule set

  • oasdiff checks on its own prints the available listings instead of the changelog rules. There is now one listing per rule set: oasdiff checks changelog for the rules breaking and changelog apply, and oasdiff checks validate for the rules validate reports (#1132). If you script oasdiff checks, add the changelog subcommand. The validate rules had no listing at all before this.

New validate lints

  • Schema constraints that nothing can satisfy are reported as errors: minimum above maximum, and the same for minLength/maxLength, minItems/maxItems, minProperties/maxProperties and minContains/maxContains (#1122).
  • type-format-mismatch: a format that belongs to a different type is reported as a warning, since it is silently ignored at runtime, for example format: date-time on an integer (#1120).

Severity and flag changes

  • request-body-enum-value-removed is now breaking by default. Removing an enum value from a request body rejects payloads that were valid before, which is an error, not an informational note (#1118).
  • --include-checks is deprecated and ignored, and the optional-checks mechanism behind it is retired. All checks now run, and severity is the only lever. If you used --include-checks to make an optional check fail the build, it no longer does: the check now reports at its default severity, so move those ids into --severity-levels with err to keep the gate. The flag still parses and prints a notice on stderr. --flatten and --max-circular-dep are likewise deprecated, each pointing at its replacement (#1119).

Misc

  • A revision starting with a dash is no longer parsed as a git option (#1125).
  • Documented that source locations require a YAML spec (#1129) and how to pass arguments safely from a script or CI (#1126).
  • Dependency bumps: kin-openapi 0.145.0, goldmark 1.8.5, yaml/v3 3.0.5.

Go package changes

  • diff.Diff carries BaseInfo and RevisionInfo (*openapi3.Info), the info object from each spec, following the same Base/Revision convention as PathsDiff and SchemaDiff. They are populated only on a non-empty diff and excluded from JSON and YAML output, so Empty() and the diff output are unchanged (#1133).
  • New checker.InfoChange, for findings whose subject is the document rather than an endpoint. It implements checker.Change like ApiChange, ComponentChange and SecurityChange, with an empty path and operation and GetSection() == "info". Code that type-switches over change types should expect it (#1133).
  • The optional-checks API is gone with the mechanism it served: GetOptionalChecks, GetOptionalRules and the WithOptionalChecks option no longer exist, and GetAllChecks returns every check. Callers that enabled optional checks should set the severity of the ids they care about instead (#1119).