Add a Maven site report (`report` goal) to the Maven plugin

PA
2d ago
+1266-8

Hi! šŸ‘‹

The diff goal is great for failing a build on breaking changes, but its result only ends up in the build log. Projects that publish a Maven site have no good place to show "what changed in our API since the last release". This PR adds a report goal to openapi-diff-maven that renders the comparison as a page of the Maven site.

What you get

Add the plugin to <reporting> and mvn site gets an OpenAPI Diff page under Project Reports. The goal also runs on its own: mvn -DoldSpec=<old spec> -DnewSpec=<new spec> org.openapitools.openapidiff:openapi-diff-maven:report writes target/reports/openapi-diff.html. A direct run needs these properties because Maven only applies <reporting> configuration during site generation. The page has:

  • a summary: both specs (title, version, location), the overall result, and how many endpoints were added, deleted, deprecated and changed;
  • the familiar What's New / What's Deleted / What's Deprecated sections, as tables;
  • What's Changed, one section per operation, with its compatibility and what changed: operation ID, parameters, request body, responses and headers, security requirements. When something breaks compatibility, the page says why (changed type, removed property, newly required property, enum values, constraints such as maxLength).

For example, from the new test fixtures:

GET /pets
List pets
Compatibility: Breaks backward compatibility
- Operation ID changed from listPets to listAllPets
- Parameters
    - Added offset in query
    - Deprecated limit in query
- Responses
    - Changed 200 OK
        - Changed application/json: schema breaks compatibility
            - Removed property [n].tag (string)
            - Changed type of [n].id: integer (int64) -> string

The goal takes the same oldSpec, newSpec, configFiles, configProps and skip parameters as diff, so both goals give the same verdict. It never fails the build; gating stays the job of diff.

<reporting>
  <plugins>
    <plugin>
      <groupId>org.openapitools.openapidiff</groupId>
      <artifactId>openapi-diff-maven</artifactId>
      <version>${openapi-diff-version}</version>
      <configuration>
        <oldSpec>https://petstore3.swagger.io/api/v3/openapi.json</oldSpec>
        <newSpec>${project.basedir}/target/openapi.yaml</newSpec>
      </configuration>
      <reportSets>
        <reportSet>
          <reports>
            <report>report</report>
          </reports>
        </reportSet>
      </reportSets>
    </plugin>
  </plugins>
</reporting>

How it's built, and why

  • Only the Maven plugin changes. Doxia is a Maven site concern, so openapi-diff-core, the CLI and the Docker image don't get any new dependency.
  • Doxia Sink instead of HtmlRender. HtmlRender writes a whole HTML document that links a remote http:// stylesheet, so it can't sit inside a site page, and browsers would block that stylesheet on an https site anyway. Writing through the sink gives the page the site's skin and menu, and every string coming from the specs is escaped.
  • Maven Reporting 4 / Doxia 2 (maven-reporting-impl 4.0.0, built for Java 8). That's what maven-site-plugin 3.21.0+ uses, and maven/pom.xml already pins 3.22.0. Older site plugins (Doxia 1) can't run reports built on it, so the README asks for 3.21.0 or newer.
  • The plugin's minimum Maven version doesn't move. I read the discussion in #489, so maven-plugin-api and maven-core stay at 3.6.0, and the generated descriptor still says requiredMavenVersion 3.6.0. The reporting library itself needs Maven 3.6.3, but so does every maven-site-plugin able to run it, and the diff goal is unaffected.
  • No absolute paths or credentials on published pages. Spec locations are shown relative to the project directory. Other files, including file: URIs, are shown by name only. URLs are shown without user info, query or fragment, any of which can carry a token.
  • "What's Deprecated" only lists newly deprecated endpoints. ChangedOpenApi#getDeprecatedEndpoints() also returns operations whose deprecation was removed. The report filters those out and says "Operation no longer deprecated" for them instead.
  • A small package-private OpenApiDiffOptionsFactory now builds the options for both goals, so their config handling can't drift apart. diff behaves exactly as before.

Testing

  • OpenApiDiffReportTest (13 tests) renders the report through a real Doxia XHTML5 sink. It covers:
    • no changes, compatible and incompatible results;
    • config files and properties;
    • every section, using a new fixture pair (report-oldspec.yaml → report-newspec.yaml);
    • which additions and deletions are marked as breaking;
    • newly deprecated vs. no-longer-deprecated operations;
    • escaping of text from the specs;
    • skip, missing configuration, a missing spec;
    • how locations are displayed, including file: URIs and URLs with credentials.
  • ./mvnw verify passes on JDK 17: 270 core tests and 26 Maven plugin tests. The plugin also compiles with --release 8, and fmt:check is clean.
  • End to end, mvn site on maven-example with maven-site-plugin 3.22.0 and the Fluido skin puts the page under Project Reports, and the standalone goal works too. maven-example now has a <reporting> section, so you can try it with ./mvnw install -DskipTests && ./mvnw -pl maven-example site.

There's no issue for this yet. I'm happy to open one first, rename the goal, or slim the page down if you'd rather keep it smaller. Thanks for taking a look!

šŸ¤– Generated with Claude Code