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.

Sample Configuration

The following is the default configuration of the extension:

{
  "enabled": false, (1)
  "semantic0": true, (2)
  "versionIncreaseAllows": { (3)
    "major": {
      "severity": "BREAKING"
    },
    "minor": {
      "severity": "NON_BREAKING"
    },
    "patch": {
      "severity": "EQUIVALENT"
    }
  },
  "onAllowed": { (4)
    "attachments": {
      "breaksVersioningRules": "false"
    }
  },
  "onDisallowed": { (5)
    "criticality": "error",
    "attachments": {
      "breaksVersioningRules": "true"
    }
  },
  "passThroughDifference": [] (6)
}
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 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.
4 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.
5 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.
6 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 to true, 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 to true (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 to true, 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 or BREAKING.

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 and new attributes described below.

old

The suffix of the old version, e.g. Beta. If regex attribute is true, this is considered a regular expression.

new

The suffix of the new version, e.g. Final. If regex 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. If true, 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/or append 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/or append 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

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.14.6</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.14.6</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>