Versions Transformation
Extension: revapi.versions
This is a generic transformation of differences based on the changes of the version between the old and new archives.
It can be useful for automatically allowing certain kinds of changes based on the version difference (e.g. allow breaking changes in a new major version).
By default, it assumes semantic versioning of the archives but can be reconfigured to support different versioning policies.
The extension is disabled by default and has to be explicitly configured to be switched on.
The documentation of the now deprecated revapi.semver.ignore transformation, which served a similar purpose in the past, is
here.
|
Please be aware that this extension may interfere with revapi.differences if both revapi.versions and revapi.differences are configured
to modify the criticality of the differences. In that case they may override each other’s changes resulting in the infamous infinite loop during
the transformation. To rectify that problem, you need to place these two differences in a
transformation block. Check out the example Allow changes that are marked as ok by other rules.
|
Sample Configuration
The following is the default configuration of the extension:
The default configuration is most probably NOT what you want, because all it does is that it adds a new
attachment on the version difference. If you want to break the build on breaking changes or define some other behavior,
you will need to supply custom onAllowed or onDisallowed configuration. See the Examples for, well,
the examples.
|
{
"enabled": false, (1)
"semantic0": true, (2)
"strictSemver": true, (3)
"versionIncreaseAllows": { (4)
"major": {
"severity": "BREAKING"
},
"minor": {
"severity": "NON_BREAKING"
},
"patch": {
"severity": "EQUIVALENT"
}
},
"onAllowed": { (5)
"attachments": {
"breaksVersioningRules": "false"
}
},
"onDisallowed": { (6)
"criticality": "error",
"attachments": {
"breaksVersioningRules": "true"
}
},
"passThroughDifference": [] (7)
}
1 | The extension is disabled by default. |
2 | semantic0 tells the extension to honor the special rules for 0.y.z versions in semantic versioning.
See below for a more detailed discussion of the attribute. |
3 | strictSemver modifies the parsing of the version. If set to false , there is no restriction on the characters in
the suffix of the version. If set to true (the default), all the parts of the version need to strictly conform to
the semver specification. |
4 | versionIncreaseAllows specifies the rules for allowed changes in different version increase situations.
x.y.z.suffix versioning is required for this to work.
By default, an increase in the major version allows breaking changes, an increase in minor version allows non-breaking changes and a patch version only allows equivalent API changes. |
5 | onAllowed specifies the modifications to be performed on differences that are deemed allowed by the version increase rules.
By default, a "marker" attachment noting that the versioning rules have been broken is set to false . |
6 | onDisallowed specifies the modifications to be performed on differences that are deemed disallowed by the version increase rules.
By default, the criticality of the difference is set to error and "marker" attachment noting that the versioning rules have been broken is set to true . |
7 | One can define a set of difference codes that are not going to be touched by this transformation and will be let through as is. This is useful when you want certain kinds of differences to never be allowed. By default, this list is empty. |
To use this default configuration, all you need to do is to specifically enable the extension:
{
"extension": "revapi.versions",
"configuration": {
"enabled": true
}
}
or in XML:
<revapi.versions>
<enabled>true</enabled>
</revapi.versions>
Properties
enabled
-
Defaults to
false
. When set totrue
, the extension is enabled and checks for the versioning rules. semantic0
-
The semantic versioning specifies that if the major version is
0
, any kind of change can happen at any time because the API is not considered stable. If set totrue
(which is the default), this attribute makes the extension honor this behavior (i.e. any difference is allowed on any kind of version increase). versionIncreaseAllows
-
Within this attribute, one can specify the rules to allow differences for different kinds of version increases.
major
-
Specifies the rules for allowing differences when a major version increases. This can either directly contain the following configuration options or be a list thereof. If more than 1 rule is specified, a difference matches if at least 1 of the rules is satisfied.
regex
-
Defaults to
false
. When set totrue
, textual properties are considered regular expressions instead of clear text. severity
-
The maximum severity of a difference to be allowed. One of
NONE
,EQUIVALENT
,NON_BREAKING
,POTENTIALLY_BREAKING
,BREAKING
. Cannot be matched as a regular expression. criticality
-
The maximum criticality of a difference to be allowed. This is one of criticalities configured in the pipeline configuration. This cannot be matched using a regular expression.
classification
-
This is a map of classifications that a difference needs to maximally have to be allowed. The keys are one of
BINARY
,SOURCE
,SEMANTIC
,OTHER
(i.e. one of compatibility types) and the values are the severities:EQUIVALENT
,NON_BREAKING
,POTENTIALLY_BREAKING
orBREAKING
. code
-
The code (i.e. the identifier) of the difference that should be allowed.
justification
-
The text of the justification that should make the difference be allowed. If
regex
is true, this is understood to be a regular expression, otherwise an exact match is used. attachments
-
The map of attachments that the difference needs to have in order to be allowed. The keys always need to match exactly, the values are interpreted either exactly or as a regular expression depending on the value of the
regex
property. inArchives
-
A list of archives in which this rule applies. These are either exact matches or regular expressions depending on the value of the
regex
attribute. The values are compared with the "base names" of the archives - i.e. the name without a version.
minor
-
This attribute specifies the allowed changes in a minor version increase. The options are the same as with the major version increase.
patch
-
This attribute specifies the allowed changes in a patch version increase. The options are the same as with the major version increase.
suffix
-
This attribute specifies the allowed changes when only a suffix. The options are the same as with the major version increase + the
old
andnew
attributes described below.old
-
The suffix of the old version, e.g.
Beta
. Ifregex
attribute is true, this is considered a regular expression. new
-
The suffix of the new version, e.g.
Final
. Ifregex
attribute is true, this is considered a regular expression.
onAllowed
-
This configuration describes how to modify the differences that we found to conform to the versioning rules and are therefore allowed.
remove
-
Defaults to
false
. Iftrue
, the difference is removed from the analysis results. classification
-
This is a map of classifications (same in the format with the classification specification in the version increase configuration). These classifications will be added to the difference classification (overwriting the pre-existing classifications, if any).
justification
-
This either the exact text of the justification that should be added to the difference, or an object with
prepend
and/orappend
keys using which you can prepend or append some text to pre-existing justification. description
-
This either the exact text of the description that should be added to the difference, or an object with
prepend
and/orappend
keys using which you can prepend or append some text to pre-existing description. criticality
-
The criticality that should be set on the difference.
attachments
-
The map of attachments that should be added to the difference, potentially overwriting any pre-existing ones.
onDisallowed
-
This has the same configuration properties as
onAllowed
but describes the modifications to be made on the disallowed differences. passThroughDifferences
-
This is a list of difference codes that should be ignored by this extension and be passed on to the next stages as is.
Examples
Define how to react on the version changes
The default configuration defines a very simple "reaction" on the version changes. It merely defines an attachment on
the differences that tells whether that difference breaks or conforms to the versioning rules. This can be useful if
you define some further "reaction" on such attachment in other extensions, but generally speaking, you will want to
define what should be done with the differences using the onAllowed
and onDisallowed
properties of
revapi.versions
.
There is a couple of things one can do with the difference:
- Changing the criticality
-
This is likely what you want to do with the disallowed differences. Learn more about criticality here.
<revapi.versions> <versionIncreaseAllows> .... </versionIncreaseAllows> <onDisallowed> <criticality>error</criticality> </onDisallowed> </revapi.versions>
If you face a situation where some version of your dependency (that you make public through your API, too) introduced some API changes that you are comfortable allowing (but that would otherwise break the build because of the more strict settings you configured for your own code), you can reset the criticality on the differences coming from such dependency using the following configuration:
<revapi.versions> <versionIncreaseAllows> <major> <inArchives> <item>org.acme:my-dep</item> </inArchives> </major> </versionIncreaseAllows> <onAllowed> <criticality>documented</criticality> </onAllowed> </revapi.versions>
- Removing the difference
-
You can choose to remove the differences. This can be used for example to ignore changes coming from a particular version of some of your dependency that you also make public in your API but that you are comfortable allowing.
You should possibly just set the criticality on such changes to a value that doesn’t break the build (like documented
in the default set of criticalities) to preserve the visibility of the changes.<revapi.versions> <versionIncreaseAllows> <major> <inArchives> <item>org.acme:my-dep</item> </inArchives> </major> </versionIncreaseAllows> <onAllowed> <remove>true</remove> </onAllowed> </revapi.versions>
- Modify justification
-
When you want to modify the justification to highlight the fact that the difference is being dealt with the way it is because of versioning rules, you can do so like this:
<revapi.versions> <versionIncreaseAllows> ... </versionIncreaseAllows> <onAllowed> <justification> <append> (conforms to versioning rules)</append> </justification> </onAllowed> <onDisallowed> <justification> <append> (breaks the versioning rules)</append> </justification> </onDisallowed> </revapi.versions>
You can also use
prepend
to prefix the pre-existing justification with some text, or you can specify a textual justification as a whole:<revapi.versions> <versionIncreaseAllows> <major> <inArchives> <item>org.acme:my-dep</item> </inArchives> </major> </versionIncreaseAllows> <onAllowed> <criticality>documented</criticality> <justification>We're assuming, you can accommodate for the changes in my-dep.</justification> </onAllowed> </revapi.versions>
- Modifying the description
-
If you don’t want to modify the
justification
for a difference but instead want to modify itsdescription
, you can do so in very much the same way as in the previous examples. Just replacejustification
withdescription
.
Don’t allow any changes between a beta, and a final version
Let’s assume that we mark the beta versions with the beta
suffix and final versions are without any suffix whatsoever.
We want to make sure that there are no API changes at all between the beta and the final version.
<revapi.versions>
<versionIncreaseAllows>
<suffix>
<regex>true</regex>
<old>[bB]eta</old>
<severity>NONE</severity>
</suffix>
</versionIncreaseAllows>
</revapi.versions>
Allow adding method to the interfaces, but no other breaking changes in non-major releases
Let’s assume that the interfaces in our library are not meant for implementation but merely for providing a public interface to the private implementations. Therefore, our policy is that we don’t consider adding methods to interfaces as a breaking change.
<revapi.versions>
<versionIncreaseAllows>
<minor>
<item>
<code>java.method.addedToInterface</code>
</item>
<item>
<severity>NON_BREAKING</severity>
</item>
</minor>
<patch>
<item>
<code>java.method.addedToInterface</code>
</item>
<item>
<severity>EQUIVALENT</severity>
</item>
</patch>
</versionIncreaseAllows>
</revapi.versions>
Allow changes that are marked as ok by other rules
Using the transformation blocks, one can configure the order in which the differences are processed by different extensions. We can take advantage of that and using more powerful transformations, like Differences Transformation, to pick and choose which changes are ok.
In this example we have a special revapi.differences
instance called manually-vetted
that is meant to capture differences that the maintainer manually examined and deemed ok for the next release.
These differences don’t conform to "the normal" set of rules otherwise required.
In Maven, one can configure the transform blocks like this:
<plugin>
<groupId>org.revapi</groupId>
<artifactId>revapi-maven-plugin</artifactId>
<version>0.15.0</version>
<configuration>
<pipelineConfiguration>
<transformBlocks>
<block>
<item>manually-vetted</item>
<item>revapi.versions</item>
</block>
</transformBlocks>
</pipelineConfiguration>
</configuration>
</plugin>
With the transform block in place, we can configure the manually vetted differences and versions extensions. Notice the multiple rules for each version increase - a difference is considered OK if at least 1 of the rules is satisfied.
<plugin>
<groupId>org.revapi</groupId>
<artifactId>revapi-maven-plugin</artifactId>
<version>0.15.0</version>
<configuration>
<analysisConfiguration>
<revapi.differences id="manually-vetted">
<attachments>
<vetted>ok</vetted>
</attachments>
<differences>
… any difference matches configured using the Differences Transformation…
</differences>
</revapi.differences>
<revapi.versions>
<versionIncreaseAllows>
<major>
<item>
<severity>BREAKING</severity>
</item>
<item>
<attachments>
<vetted>ok</vetted>
</attachments>
</item>
</major>
<minor>
<item>
<severity>NON_BREAKING</severity>
</item>
<item>
<attachments>
<vetted>ok</vetted>
</attachments>
</item>
</minor>
<patch>
<item>
<severity>EQUIVALENT</severity>
</item>
<item>
<attachments>
<vetted>ok</vetted>
</attachments>
</item>
</patch>
</versionIncreaseAllows>
</revapi.versions>
</analysisConfiguration>
</configuration>
</plugin>