feat(spec-merge): add alternative DEEP merge mode with by-method path merging and configurable conflict resolution strategy
Note: This change should be fully backwards compatible as it is entirely opt-in for now.
Problem
When merging multiple OpenAPI specs, the previous MergedSpecBuilder overwrote a path
whenever two specs defined the same URL path. If one spec defined GET /pets and another
defined POST /pets, one of the operations was silently lost.
What changed
- Merge paths by HTTP method: colliding paths now combine their operations
(e.g.GETfrom one file +POSTfrom another) instead of overwriting. - Path-level data preserved: path-level
parameters,servers, andx-vendor
extensions are merged (first definition wins on conflict); server URLs are de-duplicated. - Only real spec files scanned: only
.yaml,.yml,.jsonare processed, in a
deterministic (sorted) order; other files are ignored. - Explicit ordered file list: you can now pass an ordered list of spec files to merge
(with an output dir) across CLI, Gradle, and Maven, instead of only scanning a directory.
Why the new mergeMode and mergeConflictStrategy
Two merge modes exist so behavior stays backward-compatible while still allowing a real merge:
-
mergeModeREF(default): the original behavior - produces a lightweight index spec where each
path is a$refback to its source file. Kept as default so existing setups don't change.DEEP: fully inlines all paths and components into one self-contained spec, with component
deduplication and the by-method path merging described above.
-
mergeConflictStrategy(only applies inDEEPmode)
When two specs define the same component name (schema, response, etc.) with different
definitions, we need a policy:WARN(default): keep the first definition and log a warning.FAIL: abort the merge. Useful in CI to catch accidental conflicts early.
Note: identical duplicate components are silently de-duplicated. Duplicate path+method
definitions always fail, since there is no valid reason for the same method on the same path
to be defined twice.
Where it's exposed
- CLI:
--merge-mode,--merge-conflict-strategy,--input-spec-files,--merged-file-output-dir - Gradle:
mergeMode,mergeConflictStrategy,inputSpecFiles,mergedFileOutputDir - Maven:
mergeMode,mergeConflictStrategy,inputSpecFiles,mergedFileOutputDir
Invalid values fail fast with a clear error. Docs and tests updated accordingly.
PR checklist
- Read the contribution guidelines.
- Run the following to build the project and update samples:
(For Windows users, please run the script in WSL)./mvnw clean package || exit ./bin/generate-samples.sh ./bin/configs/*.yaml || exit ./bin/utils/export_docs_generators.sh || exit
Commit all changed files.
This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
These must match the expectations made by your contribution.
You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example./bin/generate-samples.sh bin/configs/java*.
IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed. - If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.
Summary by cubic
Adds a DEEP merge mode that merges paths by HTTP method, preserves path-level data, resolves external $refs for a self-contained spec, and uses a configurable conflict strategy. Also supports ordered input spec lists and root metadata merging across CLI, Gradle, and Maven, while keeping REF as the original $ref-based mode.
-
Bug Fixes
- Merge path items by method on collisions; detect duplicate path+method and apply
mergeConflictStrategyin DEEP; REF keeps last. - Preserve path-level parameters and vendor extensions; dedupe params by
$ref; dedupe servers and addhttp://localhost:8080if none. - Resolve external
$refs intocomponentsin DEEP; CLI forwards--authfor authenticated refs. - Propagate root-level
securityto operations unless overridden; warn-and-rename duplicateoperationIds. - Merge root-level
tags,externalDocs,webhooks(first-wins) andcomponents.pathItems(OAS 3.1). - Scan only
.yaml,.yml,.jsondeterministically; respectinputSpecFilesorder; keep JSON if the first valid spec is JSON. - Validate
--merge-conflict-strategyregardless of mode; Gradle cleans output before writing; wheninputSpecFilesis set, skip directory scan; fail fast on empty. - Validate OpenAPI version in DEEP: enforce strict MAJOR.MINOR.PATCH semver, reject missing/malformed values (e.g.,
3.0,3.0.3.1, leading zeroes), and prevent mixing different major versions with clear errors.
- Merge path items by method on collisions; detect duplicate path+method and apply
-
New Features
- Add
mergeMode(REF,DEEP) andmergeConflictStrategy(WARN,FAIL) in CLI (--merge-mode,--merge-conflict-strategy), Gradle, and Maven; invalid values fail fast. - Support an explicit ordered list of spec files to merge across CLI (
--input-spec-fileswith--merged-file-output-dir), Gradle (inputSpecFiles,mergedFileOutputDir), and Maven (inputSpecFiles,mergedFileOutputDir); order overrides directory scanning. - Add merged spec Info fields on all three (
mergedFileInfoName,mergedFileInfoDescription,mergedFileInfoVersion); Gradle also addsmergedFileName(defaults tomerged).
- Add