Differences Transformation
Extension: revapi.differences
This is a swiss-army knife extension for manipulating the differences. It can be used to reclassify or ignore the differences, and it also can be used to provide justifications to the found differences and add custom match parameters (aka attachments) to them.
It supersedes the revapi.ignore
and revapi.reclassify
extensions and
provides new functionality that enables sharing the configuration for build-time and report-time.
Sample Configuration
[
{
"extension": "revapi.differences",
"configuration": {
"justification": "This change is necessary to fix bug #1234",
"criticality": "highlight",
"differences": [
{
"code": "java.method.addToInterface",
"new": "method void com.acme.ToolInterface::setup()"
},
{
"code": "java.method.removed",
"old": {
"matcher": "java",
"match": "interface com.acme.ToolInterface { void ^initialize(); }"
},
"justification": "As part of fix for #1234, we thought renaming this method is just fine."
},
]
}
}
]
<revapi.differences>
<justification>This change is necessary to fix bug #1234</justification>
<criticality>hightlight</criticality>
<differences>
<item>
<code>java.method.addToInterface</code>
<new>method void com.acme.ToolInterface::setup()</new>
</item>
<item>
<code>java.method.removed</code>
<old>method void com.acme.ToolInterface::initialize()</old>
<justification>As part of fix for #1234, we thought renaming this method is just fine.</justification>
</item>
</differences>
</revapi.differences>
Properties
justification
-
The justification that will be added to all the matching differences, if the concrete difference match recipe doesn’t provide its own justification.
criticality
-
The criticality that will be assigned to all of the matching differences, if the concrete difference match recipe doesn’t provide its own criticality.
ignore
-
If
true
, all the matching differences will be ignored. This can be locally redefined in each difference match recipe. classify
-
The classification for all the matching differences. This can be locally redefined in each difference match recipe.
attachments
-
The new attachments (match parameters) to add to all the matching differences. This is merged with any new attachments locally defined at each difference match recipe with the local definitions taking precedence.
differences
-
The list of difference match recipes. Each of the recipes can match and modify zero or more differences reported by the analysis.
The match recipe consists of the following properties:
regex
-
If
true
(the default isfalse
), thecode
,old
,new
and any values of the additional properties are understood to be java regular expressions. code
-
Specifies the API problem code to ignore. This is property mandatory.
old
-
Specifies the old element of the problem to ignore either using (a regex of) its textual representation or as a matcher expression. This property is optional.
new
-
Specifies the new element of the problem to ignore either using (a regex of) its textual representation or as a matcher expression. This property is optional.
justification
-
This can used to describe why this change was necessary. This can be used in the reports to give additional detail for why a change was necessary.
criticality
-
This is used to assign the criticality of the difference for the overall analysis result. This must be one of the configured criticality names.
attachments
-
This can be used to add additional attachments (match parameters) to the difference that can be used by the difference transformations down the line. It is an ordinary key-value map with string keys and values.
- additional properties
-
The analyzers can define additional match parameters on the differences that can be used to further focus the ignore rule. For java, see the list of the detected differences. Such additional properties always have a string value. These are the attachments (match parameters) defined by the analyzer or by previously run difference transforms.
To learn more about the element matching, read Matching Elements. |
Examples
Use a single configuration for build and reports
Many times (if not always) library authors want to report on what API changes have been made in the API. Ideally, such report should also include the reasons for making those API changes. At the same time, during the builds, one wants to ignore the intentional, justified changes, but fail the build on the new ones (until they are deemed necessary or rolled back).
Let’s see how we can configure Revapi maven plugin to use a single configuration during the build to fail on new API changes and for reporting all the intentional API changes.
Let’s put all our intentional API changes into a separate JSON file and call it api-changes.json
. This file contains
multiple Revapi configurations, one for each released version:
{
"0.2.0": [
{
"extension": "revapi.differences",
"id": "intentional-api-changes", (1)
"configuration": {
"differences": [
{
"code": "java.method.addedToInterface",
"new": "method com.acme.Tooling::setup()",
"justification": "The original `initialize()` method was never meant to be public."
},
{
"code": "java.method.removed",
"old": "method com.acme.Tooling::initialize()",
"justification": "This method was made public by accident."
}
]
}
}
]
}
1 | The explicit extension instance ID gives us the possibility to merge it with snippets coming from other places
like pom.xml . |
Equipped with this file, we can configure the Maven plugin to read it for both build and reporting.
<build>
<plugins>
<plugin>
<groupId>org.revapi</groupId>
<artifactId>revapi-maven-plugin</artifactId>
<configuration>
<analysisConfiguration>
<revapi.differences id="intentional-api-changes"> (1)
<ignore>true</ignore>
</revapi.differences>
</analysisConfiguration>
<configurationFiles>
<configurationFile>
<path>${basedir}/api-changes.json</path>
<roots>
<root>${project-version-without-snapshot}</root>
</roots>
</configurationFile>
</configurationFiles>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.revapi</groupId>
<artifactId>revapi-maven-plugin</artifactId>
<configuration>
<configurationFiles> (2)
<configurationFile>
<path>${basedir}/api-changes.json</path>
<roots>
<root>${project-version-without-snapshot}</root>
</roots>
</configurationFile>
</configurationFiles>
<reportSeverity>nonBreaking</reportSeverity>
</configuration>
</plugin>
</plugins>
</reporting>
1 | We’re specifying that we’re updating the configuration of the same instance as in the json file. This means that
the pom.xml adds the ignore = true to the configuration of revapi.differences . Having ignore set to true
"globally" in the whole configuration of the revapi.differences extension instance with the specific ID, means that
all differences specified will be ignored during the API checks. |
2 | For reporting, we’re referencing the same configuration file as for building, but this time we’re not adding any
modifications to the configuration. As such we let the revapi.differences update the justifications on all matching
differences but leave it in the report. Thus the resulting maven report contains the justifications specified in our
configuration file. |
Add custom attachments for reporting purposes
It can be useful to be able to for example link API changes to the bug tracker issues for which they were introduced. One way of doing it is to add custom attachments to the intentional changes and have a custom reporter (or just a text reporter template) to render the attachment appropriately.
Let’s just use the configuration file from the previous example and enhance it with some additional attachments.
{
"0.2.0": [
{
"extension": "revapi.differences",
"id": "intentional-api-changes", (1)
"configuration": {
"differences": [
{
"code": "java.method.addedToInterface",
"new": "method com.acme.Tooling::setup()",
"justification": "The original `initialize()` method was never meant to be public.",
"attachments": {
"jira-id": "ACME-42"
}
},
{
"code": "java.method.removed",
"old": "method com.acme.Tooling::initialize()",
"justification": "This method was made public by accident."
"attachments": {
"jira-id": "ACME-42"
}
}
]
}
}
]
}
This way the reporter has a way of identifying the API changes that went in as part of the fix of a JIRA issue
ACME-42
and can use that information as it sees fit.