Releases: oasdiff/oasdiff
Release list
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.
v1.30.0
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}-setchecks: adding a bound rejects values the previous contract accepted, the same shape asbecame-enum,const-addedandpattern-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-addedandresponse-property-any-of-addednow matchresponse-body-one-of-added, andresponse-property-enum-value-addedis an error: the server may return a value no client was written to handle (usex-extensible-enumfor value sets that are meant to grow).response-property-pattern-removedis an error andresponse-property-pattern-changeda 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-removedandoptional-response-header-removed: a conforming client already tolerates the element's absence.request-body-all-of-removed,request-property-all-of-removedandresponse-required-property-became-not-write-onlydrop to info on similar reasoning, andresponse-media-type-name-changedbecomes 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
oneOfthat keeps the original schema as one of the alternatives is now the newrequest-body-wrapped-in-one-of-original-preserved/response-body-wrapped-in-one-of-original-preservedat 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,multipleOfanduniqueItemswere 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 themultipleOfgeneralized/specialized cases where one bound divides the other. ThemultipleOfcomparison is exact rational arithmetic, so0.3to0.1is 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)
trueandfalseschemas 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 becomingfalseaccepts nothing: the new*-schema-became-falsechecks 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{}andtrueis a document edit with no contract effect: visible inoasdiff diff, silent in the changelog.- Closing a tuple with
items: falseis detected (#1203). Addingitems: falseto a schema that had noitemsinvalidates every array longer than the prefix and previously reported nothing. - A schema arriving as
falseortrueis classified, not just counted (#1191). A media-type schema added asfalsereports asschema-became-falseinstead of an informational "schema added", and one added astrueor{}(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).
prefixItemsentries 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]), andoasdiff diffshows per-position modifications. - prefixItems verdicts follow the items schema (#1200). An entry that restates the
itemsschema 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 whenitemsis absent but lengthens the tuple whenitemsisfalse, 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
queryfield andadditionalOperationsmap 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-plusandboolean-schema-with-other-keywordsjoinoasdiff validate.
New commands
oasdiff breaking-fileschecks 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.yamlships inexamples/.oasdiff checks changelog coverageshows 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.--tagsfilters by direction, area, kind, action and status;--patternslists the claim patterns. Uncovered edits and stale waivers fail the build, so the coverage listing is enforced, not aspirational.
Misc
oasdiff validatefindings always carry a line and column (#1177).duplicate-required-fieldandduplicate-tagreported a file with no location.- An unsupported
--templateformat is rejected before the specs load (#1178), like the equivalent--colormismatch, 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 itsDirection,Area,Kind,Effect(widens, narrows, incomparable, unknown, none, violation) andGuards, andchecker.BackwardCompatibilityRulecarries 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) andchecker/coverage(the audit) are new packages. diffunderstands boolean schemas (#1191).SchemaDiff.AlwaysDiffcarries transitions of the JSON Schema boolean form, andSchemaRefsValidationEquivalenttreatstrueas the empty schema while keepingfalsedistinct.diffpairsprefixItemsby index (#1192).SchemaDiff.PrefixItemsDiffreports positional modifications instead of set-matched additions and deletions; consumers of the diff JSON seemodifiedentries where reorders previously produced nothing.PrefixItemsValidationEquivalentreports whether two schemas validate every prefix-covered position identically (#1200), andOneOfWrappingDiff.OriginalPreservedreports whether a oneOf wrapping keeps the base schema as an alternative (#1174).- Color primitives moved to a
colorizepackage (#1174).checkerkeeps type and constant aliases, so existing callers compile unchanged. - Breaking: the unused
checker/generatorpackage is removed (#1174).
Misc
- Builds lift to Go 1.26.7 (#1202). The
toolchaindirective picks up standard-library security fixes (crypto/tls, net/url, html/template, encoding/asn1) for release binaries,go installbuilds and local builds alike;golang.org/x/textmoves past CVE-2026-56852.
v1.29.1
--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
$refis 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 exampleschema: { $ref: '#/components/schemas/Pet' }under a response) reading the unmerged original. Since the diff traverses schemas through their uses, anallOfreached under a$refsurvived--flatten-allofcompletely, andoasdiff breaking --flatten-allofcould produce output identical to running without the flag. The merged content is now written into the schema every$refalready points at, so flattening takes effect at the point of use. Serialized output ofoasdiff flattenwas 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 siblingallOfbranch previously hid a change, and changes under such anallOfstop 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
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
itemSchematyping 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, andresponse-body-media-type-item-schema-removed-untypedare errors; the reverse directions are info.docs/OPENAPI-31.mdgained 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
allOfare capped at warning, with an explanation. Without--flatten-allof, oasdiff comparesallOfbranches 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 adisclaimersarray (["all-of-not-flattened"]) in json and yaml output. A level you set explicitly via--severity-levelsstill wins over the cap. request-body-all-of-addednow reports warning instead of error as a consequence: on its own fixture,--flatten-allofreports 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-removedwas 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 breakingreports the same set of changes as before, but pipelines gating on--fail-on ERRthat 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 noallOfat all, so a closed schema became open and whole validation branches disappeared from--flatten-allofcomparisons. They now pass through, andoasdiff diffbetween a spec and its flattened output no longer reports the loss. A keyword on anallOfsubschema is still not merged;docs/ALLOF.mddocuments 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 unversionedoasdiff-cli. Nothing identifying is added; this names the client, not the user.
Go package changes
Disclaimers surface
- Breaking: the
checker.Changeinterface gainedGetDisclaimers() []Disclaimer(#1147). Any external implementation ofChangemust add the method (returning nil is fine;ComponentChangeandSecurityChangedo exactly that).ApiChangecarries a newDisclaimersfield and aWithDisclaimersmethod, which is additive and de-duplicating, and the newchecker.Disclaimertype serializes by name (all-of-not-flattened) in json and yaml.
Misc
- New
diff.MediaTypeDiff.ItemSchemaDifffield (#1139). A*SchemaDifffor the OpenAPI 3.2itemSchema, serialized asitemSchemain diff output, alongside the existingSchemaDifffor the whole-body schema.
v1.28.0
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.Fieldsbecame a slice, and a document's origin tree is now retained only when a$refcan 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.Fieldsis nowFieldLocations, notmap[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)
Lookupreturns the same(Location, bool)pair, so the surrounding code is unchanged. Ranging overFieldsand taking its length still work. EachLocationcarries its ownName, which is what the lookup matches on.JSON and YAML output are unaffected:
FieldLocationsmarshals as the same name-keyed object the map produced, so anything consumingoasdiff diff -f jsonsees no difference.
v1.27.0
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.versionon 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), andapi-major-version-not-bumped(it moved, but not the major). All default to INFO, so nothing fails on their own; raise them toerrwith--severity-levelsto enforce, or set them tononeto 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 barev1, or not at all are left alone. Below1.0.0a 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 checkson its own prints the available listings instead of the changelog rules. There is now one listing per rule set:oasdiff checks changelogfor the rulesbreakingandchangelogapply, andoasdiff checks validatefor the rulesvalidatereports (#1132). If you scriptoasdiff checks, add thechangelogsubcommand. The validate rules had no listing at all before this.
New validate lints
- Schema constraints that nothing can satisfy are reported as errors:
minimumabovemaximum, and the same forminLength/maxLength,minItems/maxItems,minProperties/maxPropertiesandminContains/maxContains(#1122). type-format-mismatch: aformatthat belongs to a different type is reported as a warning, since it is silently ignored at runtime, for exampleformat: date-timeon an integer (#1120).
Severity and flag changes
request-body-enum-value-removedis 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-checksis 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-checksto 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-levelswitherrto keep the gate. The flag still parses and prints a notice on stderr.--flattenand--max-circular-depare 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.DiffcarriesBaseInfoandRevisionInfo(*openapi3.Info), the info object from each spec, following the sameBase/Revisionconvention asPathsDiffandSchemaDiff. They are populated only on a non-empty diff and excluded from JSON and YAML output, soEmpty()and the diff output are unchanged (#1133).- New
checker.InfoChange, for findings whose subject is the document rather than an endpoint. It implementschecker.ChangelikeApiChange,ComponentChangeandSecurityChange, with an empty path and operation andGetSection() == "info". Code that type-switches over change types should expect it (#1133). - The optional-checks API is gone with the mechanism it served:
GetOptionalChecks,GetOptionalRulesand theWithOptionalChecksoption no longer exist, andGetAllChecksreturns every check. Callers that enabled optional checks should set the severity of the ids they care about instead (#1119).