Skip to content

sitepark/versioning

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

75 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

codecov Known Vulnerabilities

Sitepark Versioning

Sitepark Versioning is a library, that provides reusable Version and VersionsSpecification classes and utilities.

Requirements

  • Java 11 or higher

Installing

Simply add this maven dependency:

	<dependency>
		<groupId>com.sitepark</groupId>
		<artifactId>versioning</artifactId>
		<version>2.0.0</version>
	</dependency>

Features

Version

Version is the supertype of all version classes. To further specify which version(s) are used the following subclasses are available:

class description
Version Any version
BaseVersion A general purpose version for external usage
ConcreteVersion A unique version. Usually an implementation detail
DatedBaseVersion BaseVersion with a date to compare
ReleaseVersion A released version
SnapshotVersion A rolling unreleased version. Can be the superset of many ConcreteVersions
ConcreteSnapshotVersion A unique unreleased version
---
version class diagram
---
%%{init: {'theme': 'dark'}}%%
classDiagram
	Version <|-- BaseVersion : extends
	Version <|-- ConcreteVersion : extends
	BaseVersion <|-- DatedBaseVersion : implements
	BaseVersion <|-- SnapshotVersion : implements
	BaseVersion <|-- ReleaseVersion : implements
	ConcreteVersion <|-- ReleaseVersion : implements
	ConcreteVersion <|-- ConcreteSnapshotVersion : implements
	class Version {
		<<interface>>
		int getMajor()
		int getMinor()
		int getIncremental()
		Branch getBranch()
		List&lt;String&gt; getQualifierList()
		boolean isSnapshot()
	}
	class BaseVersion {
		<<interface>>
	}
	class ConcreteVersion {
		<<interface>>
		BaseVersion asBaseVersion()
	}
	class DatedBaseVersion {
		LocalDateTime getDate()
		BaseVersion asUndated()
	}
	class SnapshotVersion {
		ReleaseVersion toRelease()
	}
	class ReleaseVersion {
		SnapshotVersion toSnapshot()
	}
	class ConcreteSnapshotVersion {
		String getTimestamp()
		int getBuildnumber()
		ReleaseVersion toRelease()
		SnapshotVersion toSnapshot()
	}

Ordering

Version comparisons are carried out in the following order of precedence until one is unequal:

  1. major - Higher major versions are considered greater.
  2. minor - Higher minor versions are considered greater.
  3. incremental - Higher incremental versions are considered greater.
  4. branch - Versions with a Branch.DEVELOP are considered greater, others are compared lexicographically.
  5. snapshot status - Release versions are considered greater than snapshot versions.
  6. qualifiers - Qualifiers are compared lexicographically (in order). A version with fewer qualifiers is considered greater.
  7. concrete snapshot timestamp - If both Versions are ConcreteSnapshotVersions their timestamps are compared lexicographically (not numerically! The result for the expected format yyyyMMdd.HHmmss is the same, but this is not enforced)
  8. concrete snapshot buildnumber - If both Versions are ConcreteSnapshotVersions the one with the higher buildnumber is considered greater.

This concludes the following statements:

1.0.0 > 1.1.0
1.1.0 > 1.1.1
1.1.1 > 1.1.1-SNAPSHOT
1.1.1-SNAPSHOT > 1.1.1-feature
1.1.1-feature > 1.1.1-feature-SNAPSHOT
1.1.1-feature-SNAPSHOT == 1.1.1-feature-20230101.010000-1
1.1.1-feature-20230101.010000-1 > 1.1.1-feature-20230102.100000-1
1.1.1-feature-20230102.100000-1 > 1.1.1-feature-20230102.100000-2

VersionParser

The VersionParser is a usefull tool to parse a Version into a String. For most cases a unconfigured instance is sufficient, hence one is available via VersionParser.DEFAULT_PARSER.
Otherwise it can be configured with VersionParser.Characteristics with these values:

final VersionParser parser = new VersionParser(

		// do not set branches (they default to Branch.DEVELOP)
		VersionParser.Characteristics.IGNORE_BRANCHES,

		// do not set any qualifiers
		VersionParser.Characteristics.IGNORE_QUALIFIERS);

Generally, a String to be parsed into a Version has to follow the following format or cause a ParseException:

"<major>.<minor>.<incremental>-<branch>-<qualifiers>"

major may be omitted if not leaving the String empty, defaults to zero (0)
minor and the leading dot (.) may be omitted, defaults to zero (0)
incremental and the leading dot (.) may be omitted, defaults to zero (0)
branch and the leading hyphon (-) may be omitted if no qualifiers are given, defaults to Branch.DEVELOP
qualifiers and the leading hyphon (-) may be ommitted

Here are some valid examples:

"1.0.0-develop"
"1"
".2"
"1.3-some_feature-release_candidate-0"
"-experimental"

There are three types of Versions a VersionParser can be used for:

Releases

If we expect/require the Strings we feed into the VersionParser to all represent ReleaseVersions, we can use the parseRelease method. This differs from the other parsing methods in such a way, that qualifiers, that would otherwise indicate the Version beeing a SnapshotVersion are not treated as such.

For example, a String like "1.0.1-SNAPSHOT" would result in a ReleaseVersion with the qualifier "SNAPSHOT". Similarly "1.0.2-20233005.1415-7" would also return a ReleaseVersion with the qualifiers "20233005.1415" and "7".

PotentialSnapshots

The parsePotentialSnapshot method does respect the "-SNAPSHOT" qualifier (if it is the last one). This means, that a String ending with this postfix will result in a SnapshotVersion and others will produce a ReleaseVersion. Either is wrapped inside a PotentialSnapshotVersion object, which allows functional style handling of both cases.

Here are some examples:

final PotentialSnapshotVersion version = VersionParser.DEFAULT_PARSER
	.parsePotentialSnapshot("1.2-SNAPSHOT");

// map each class individually to a different one
final ReleaseVersion release = version.mapEither(
		Snapshot::toRelease,
		Function.identity());

// execute code only if the version is a certain type
version.ifIsRelease(this::doSomethingWithReleaseVersion);

// throw an exception if the version is an unexpected type
final SnapshotVersion snapshot = version.getSnapshotOrElseThrow(
		() -> new IllegalArgumentException(
				"this process requires snapshot versions!"));

// transform it into an Optional for one type
version.getRelease()
	.flatMap(this::tryGetSomethingForReleaseVersion)
	.orElseGet(this::getDefaultValue);

// or simply get the wrapped version regardless of it's class
final BaseVersion base = version.get();

PotentialConcreteSnapshots

The parsePotentialConcreteSnapshot method is simmilar to PotentialSnapshots. If a given String ends with two qualifiers that match the format explained below a ConcreteSnapshotVersion will be constructed and a ReleaseVersion otherwise. The result is then wrapped inside a PotentialConcreteSnapshotVersion object, allowing functional style handling of either case.

Format: the second to last qualifier requires eight digits, a dot and then 6 more digits. Directly followed by a qualifier, that only consists of digits. Or as described by a regular expression: -\d{8}\.\d{6}-\d+$.
This is based on maven naming unique snapshot versions by replacing "-SNAPSHOT" with the build date ("Etc/UTC" timezone, yyyyMMdd.HHmmss format) and a build number.

final PotentialConcreteSnapshotVersion version = VersionParser.DEFAULT_PARSER
	.parsePotentialConcreteSnapshot("1.2-20210129.214836");

// map each class individually to a different one
final BaseVersion base = version.mapEither(
		ConcreteSnapshot::asBaseVersion,
		Function.identity());

// execute code only if the version is a certain type
version.ifIsRelease(this::doSomethingWithReleaseVersion);

// throw an exception if the version is an unexpected type
final ConcreteSnapshotVersion snapshot = version.getSnapshotOrElseThrow(
		() -> new IllegalArgumentException(
				"this process requires concrete snapshot versions!"));

// transform it into an Optional for one type
version.getSnapshot()
	.flatMap(this::tryGetSomethingForConcreteSnapshotVersion)
	.orElseGet(this::getDefaultValue);

// or simply get the wrapped version regardless of it's class
final ConcreteVersion concrete = version.get();

VersionFormatter

To flixibly transform Versions to Strings a configurable VersionFormatter can be used.
This class takes a String as format, which determines how Versions feed into it are transformed. Such a String may contain the following keywords surrounded by colons (:):

keyword description
MAJOR the major
MINOR the minor
INCREMENTAL the incremental
FEATURE the feature Branch (if present)
SNAPSHOT "SNAPSHOT", if the version is a snapshot
TIMESTAMP the timestamp of a ConcreteSnapshotVersion
BUILDNUMBER the buildnumber of a ConcreteSnapshotVersion
QUALIFIERS all hyphon-separated (-) qualifiers (if any)
final VersionFormatter formatter = new VersionFormatter(":MAJOR:.:MINOR:.:INCREMENTAL:");
formatter.format(onePointTwo); // -> "1.2.0"

Keywords also may contain a prefix after the first colon (:), which is prepended if the resolution of the keyword is not empty:

final VersionFormatter formatter = new VersionFormatter(":MAJOR:.:MINOR:.:INCREMENTAL::-BRANCH:");
formatter.format(onePointTwo); // -> "1.2.0"
formatter.format(onePointTwoFeature); // -> "1.2.0-feature"

To include a literal colon (:) two (::) may be used.

For most usecases the preconfigured constants should be sufficient (which are also used by the toString implementations):

  • VersionFormatter.DEFAULT_BASE_VERSION_FORMATTER
  • VersionFormatter.DEFAULT_CONCRETE_VERSION_FORMATTER

VersionTypes

The VersionTypes class can be usefull for an api to have it's user specify kinds of versions.

As an example, a Repository class may contain a method to query certain versions:

public class Repository {

	public List<BaseVersion> queryVersions(final VersionTypes types) {
		// ...
	}

	// ...
}

which then can be used like this to retrieve only the desired versions:

final List<BaseVersion> developReleases = repository.queryVersions(
		new VersionTypes(
				VersionTypes.PublicationStatusType.RELEASES,
				VersionTypes.BranchType.DEVELOP));

final List<BaseVersion> developSnapshots = repository.queryVersions(
		VersionTypes.ONLY_DEVELOP_SNAPSHOTS);

final List<BaseVersion> all = repository.queryVersions(VersionTypes.ALL);

final List<BaseVersion> none = repository.queryVersions(VersionTypes.NONE);

VersionsSpecification

A VersionsSpecification defines a subset of Versions. Divided into Branches they contain ExplicitVersions and/or VersionRanges.
They can be created either manually via VersionsSpecificationBuilder or from Strings with a VersionsSpecificationParser. The syntax used for it is based on Maven's VersionRange's:

ExplicitVersions follow the same rules as for the VersionParser.
A VersionRange consists of exactly one of these lower and upper Boundarys* separated by a comma (,):

Name Syntax Description
ExclusiveLowerBoundary (<version> x > version
InclusiveLowerBoundary [<version> x >= version
UnlimitedLowerBoundary ( any x
ExclusiveUpperBoundary <version>) x < version
InclusiveUpperBoundary <version>] x <= version
UnlimitedUpperBoundary ) any x

* The Boundarys may not both be unlimited.

Each Version in a VersionsSpecification-String may be written in any way the VersionParser understands. Spaces are ignored (except inside of Versions). All Versions of a VersionRange have to define the same Branch.

Here are some examples for valid VersionsSpecifications:

String Explaination
1.0 x == 1.0.0.
(, 1.0] x <= 1.0.0
[1.2, 1.3] 1.2.0 <= x <= 1.3.0
[1.0, 2.0) 1.0.0 <= x < 2.0.0
[1.5, ) x >= 1.5.0
(, 1.0], [1.2,) x <= 1.0.0 or x >= 1.2.0
(, 1.1), (1.1, ) x != 1.1.0
[1.0-feature, ) x >= 1.0 of the branch "feature"
[1.0-feature, 2.0-feature) 1.0 <= x < 2.0 of the branch "feature"
(, 1.2), 1.2-feature x < 1.2.0 or 1.2.0-feature

Development

You will need:

  • Java 11 or higher
  • Maven 3 or higher

Check this repository out

git clone git@github.com:sitepark/versioning.git
cd versioning

Add or edit the code and install it

mvn install

Update versioning in the project you want to use it in

mvn package

And that's it!
If you build something others could find usefull as well please consider opening a pull request.

Contributing

We accept pull requests and respond to issues via GitHub. There are some guidlines which will make applying PRs easier for us:

  • respect the code style
  • provide JUnit tests for your changes
  • add javadoc comments for public classes and methods, conforming to other comments (also see oracles bestpractices)
  • create minimal diffs - disable on save actions like reformat source code or organize imports. If you feel the source code should be reformatted create a separate PR for this change.

The pipeline will fail if either pmd, spotbugs or checkstyle are violated, javadocs are missing or invalid or any tests fail. To check this before commiting run mvn verify.

License

This project is licensed under the MIT License.

About

reusable Version and VersionsSpecification classes and utilities

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages