From 5d88d3badfeedea455ddba0e9c71fb2f17c80db6 Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Mon, 17 Feb 2020 23:02:00 +0100
Subject: [PATCH] docs: improve versioning docs

---
 docs/usage/configuration-options.md        | 14 ---------
 lib/versioning/cargo/index.ts              |  7 +++++
 lib/versioning/cargo/readme.md             | 26 ++--------------
 lib/versioning/composer/index.ts           | 10 ++++++
 lib/versioning/composer/readme.md          | 23 --------------
 lib/versioning/docker/index.ts             |  6 ++++
 lib/versioning/docker/readme.md            | 34 +++++---------------
 lib/versioning/git/index.ts                |  4 +++
 lib/versioning/git/readme.md               |  1 +
 lib/versioning/hashicorp/index.ts          |  7 +++++
 lib/versioning/hashicorp/readme.md         | 26 ++--------------
 lib/versioning/hex/index.ts                |  5 +++
 lib/versioning/hex/readme.md               | 26 +---------------
 lib/versioning/ivy/index.ts                |  5 +++
 lib/versioning/loose/index.ts              |  4 +++
 lib/versioning/loose/readme.md             | 24 +--------------
 lib/versioning/maven/index.ts              |  9 ++++++
 lib/versioning/maven/readme.md             | 25 ++-------------
 lib/versioning/node/index.ts               |  4 +++
 lib/versioning/node/readme.md              | 25 ++-------------
 lib/versioning/npm/index.ts                | 10 ++++++
 lib/versioning/npm/readme.md               | 26 ++--------------
 lib/versioning/nuget/index.ts              |  6 ++++
 lib/versioning/nuget/readme.md             | 21 +------------
 lib/versioning/pep440/index.ts             |  5 +++
 lib/versioning/pep440/readme.md            | 23 ++------------
 lib/versioning/poetry/index.ts             |  5 +++
 lib/versioning/poetry/readme.md            | 28 ++---------------
 lib/versioning/regex/index.ts              |  4 +++
 lib/versioning/regex/readme.md             | 36 ++++++++++++++++++++++
 lib/versioning/ruby/index.ts               |  9 ++++++
 lib/versioning/ruby/readme.md              | 23 +-------------
 lib/versioning/semver/index.ts             |  4 +++
 lib/versioning/semver/readme.md            | 22 ++-----------
 lib/versioning/swift/index.ts              |  5 +++
 lib/versioning/swift/readme.md             |  1 +
 test/versioning/versioning-readmes.spec.ts | 25 ++++++++++-----
 37 files changed, 192 insertions(+), 346 deletions(-)
 create mode 100644 lib/versioning/git/readme.md
 create mode 100644 lib/versioning/regex/readme.md
 create mode 100644 lib/versioning/swift/readme.md

diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index d0d7b49a22..bec28a07a2 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -1577,20 +1577,6 @@ Usually, each language or package manager has a specific type of "version scheme
 
 By exposing `versionScheme` to config, it allows you to override the default version scheme for a package manager if you really need. In most cases it would not be recommended, but there are some cases such as Docker or Gradle where versioning is not strictly defined and you may need to specify the versioning type per-package.
 
-For the `regex` `versionScheme`, will accept a regex string after a colon, for example:
-
-```json
-{
-  "versionScheme": "regex:^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(?<prerelease>[^.-]+)?(-(?<compatibility>.*))?$"
-}
-```
-
-The valid capture groups for the `regex` `versionScheme` are:
-
-- `major`, `minor`, and `patch`: at least one of these must be provided. When determining whether a package has updated, these values will be compared in the standard semantic versioning fashion. If any of these fields are omitted, they will be treated as if they were `0` -- in this way, you can describe versioning schemes with up to three incrementing values.
-- `prerelease`: this value, if captured, will mark a given release as a prerelease (eg. unstable). If this value is captured and you have configured `"ignoreUnstable": true`, the given release will be skipped.
-- `compatibility`: this value defines the "build compatibility" of a given dependency. A proposed Renovate update will never change the specified compatibility value. For example, if you are pinning to `1.2.3-linux` (and `linux` is captured as the compatbility value), Renovate will not update you to `1.2.4-osx`.
-
 ## vulnerabilityAlerts
 
 Use this object to customise PRs that are raised when vulnerability alerts are detected (GitHub-only). For example, to configure custom labels and assignees:
diff --git a/lib/versioning/cargo/index.ts b/lib/versioning/cargo/index.ts
index aa21ac9c57..1d959ec210 100644
--- a/lib/versioning/cargo/index.ts
+++ b/lib/versioning/cargo/index.ts
@@ -1,6 +1,13 @@
 import { api as npm } from '../npm';
 import { VersioningApi, NewValueConfig } from '../common';
 
+export const displayName = 'Cargo';
+export const urls = [
+  'https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html',
+];
+export const supportsRanges = true;
+export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
+
 const isVersion = (input: string): string | boolean => npm.isVersion(input);
 
 function convertToCaret(item: string): string {
diff --git a/lib/versioning/cargo/readme.md b/lib/versioning/cargo/readme.md
index cadd50a919..37ce59d515 100644
--- a/lib/versioning/cargo/readme.md
+++ b/lib/versioning/cargo/readme.md
@@ -1,31 +1,11 @@
-# Cargo versioning
-
-## Documentation and URLs
-
-https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
-
-## What type of versioning is used?
-
-Cargo uses [Semantic Versioning 2.0](https://semver.org).
-
-## Are ranges supported? How?
+Cargo versioning uses [Semantic Versioning 2.0](https://semver.org).
 
 Cargo supports ranges in a similar manner to npm, but not identical. The important differences are:
 
-##### Use of commas
+**Use of commas**
 
 Multiple version requirements can also be separated with a comma, e.g. `>= 1.2, < 1.5`. We interpret this to mean AND.
 
-##### No exact versions unless using equals =
+**No exact versions unless using equals =**
 
 In Cargo, `1.2.3` doesn't mean "exactly 1.2.3", it actually means `>=1.2.3 <2.0.0`. So this is like the equivalent of `^1.2.3` in npm.
-
-## Range Strategy support
-
-Cargo versioning should support all range strategies - pin, replace, bump, extend.
-
-## Implementation plan/status
-
-- [x] Add cargo2npm and npm2cargo functions to leverage existing npm semver logic
-- [x] Exact version support
-- [x] Range support
diff --git a/lib/versioning/composer/index.ts b/lib/versioning/composer/index.ts
index d1f7b686d3..065007b3ac 100644
--- a/lib/versioning/composer/index.ts
+++ b/lib/versioning/composer/index.ts
@@ -3,6 +3,16 @@ import { logger } from '../../logger';
 import { api as npm } from '../npm';
 import { VersioningApi, NewValueConfig } from '../common';
 
+export const displayName = 'Composer';
+export const urls = [
+  'https://getcomposer.org/doc/articles/versions.md',
+  'https://packagist.org/packages/composer/semver',
+  'https://madewithlove.be/tilde-and-caret-constraints/',
+  'https://semver.mwl.be',
+];
+export const supportsRanges = true;
+export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
+
 function padZeroes(input: string): string {
   const sections = input.split('.');
   while (sections.length < 3) {
diff --git a/lib/versioning/composer/readme.md b/lib/versioning/composer/readme.md
index f545f56c70..f47e656cba 100644
--- a/lib/versioning/composer/readme.md
+++ b/lib/versioning/composer/readme.md
@@ -1,18 +1,5 @@
-# Composer versioning
-
-## Documentation and URLs
-
-https://getcomposer.org/doc/articles/versions.md
-https://packagist.org/packages/composer/semver
-https://madewithlove.be/tilde-and-caret-constraints/
-https://semver.mwl.be
-
-## What type of versioning is used?
-
 Composer uses Semver-like versioning, however some package authors may use versions that are not completely valid, e.g. `1.2` instead of `1.2.0`.
 
-## Are ranges supported? How?
-
 Composer supports ranges in a similar manner to npm, but not identical. The main difference is with tilde ranges.
 
 Tilde ranges with "short" versions are different to npm. e.g.
@@ -20,13 +7,3 @@ Tilde ranges with "short" versions are different to npm. e.g.
 `~4` is equivalent to `^4` in npm
 `~4.1` is equivalent to `^4.1` in npm
 `~0.4` is equivalent to `>=0.4 <1` in npm
-
-## Range Strategy support
-
-Composer versioning should support all range strategies - pin, replace, bump, extend.
-
-## Implementation plan/status
-
-- [x] Add composer2npm and npm2composer functions to leverage existing npm semver logic
-- [x] Exact version support
-- [x] Range support
diff --git a/lib/versioning/docker/index.ts b/lib/versioning/docker/index.ts
index 62a6536f55..fd7ba544f2 100644
--- a/lib/versioning/docker/index.ts
+++ b/lib/versioning/docker/index.ts
@@ -1,6 +1,12 @@
 import * as generic from '../loose/generic';
 import { VersioningApi } from '../common';
 
+export const displayName = 'Docker';
+export const urls = [
+  'https://docs.docker.com/engine/reference/commandline/tag/',
+];
+export const supportsRanges = false;
+
 function parse(version: string): any {
   const versionPieces = version.replace(/^v/, '').split('-');
   const prefix = versionPieces.shift();
diff --git a/lib/versioning/docker/readme.md b/lib/versioning/docker/readme.md
index 21c5db0747..35eb8194bf 100644
--- a/lib/versioning/docker/readme.md
+++ b/lib/versioning/docker/readme.md
@@ -1,35 +1,15 @@
-# Docker versioning
+Docker doesn't really have _versioning_, instead it supports "tags" and these are usually used by Docker image authors as a form of versioning.
 
-## Documentation and URLs
+This Docker versioning implementation in Renovate is designed to handle the most common _conventions_ used in tagging images. In particular, it treats the text after the first hyphen as a type of platform/compatibility indicator.
 
-Docker doesn't really use _versioning_, but it supports "tags" and these are usually used by Docker image authors as a form of versioning.
+For example, many images include images with the "-alpine" suffix, e.g. the official `node` Docker image includes tags like `12.15.0-alpine` which is _not_ compatible with `12.15.0` or `12.15.0-stretch`. This means users only want/expect upgrades to `12.16.0-alpine` and not `12.16.0` or `12.16.0-stretch`.
 
-This "versioning" implementation is essentially a creation of Renovate to handle the default _conventions_ used in tagging most images. In particular, it treats the text after the first hyphen as a type of platform/compatibility indicator.
+Similarly, a user with `12.14` expects to be upgraded to `12.15` and not `12.15.0`.
 
-For example, many images include images with the "-alpine" suffix, e.g. the offical `node` Docker image includes tags like `8.14.0-alpine` which is _not_ compatible with `8.14.0` or `8.14.0-stretch`. This means users only want/expect upgrades to `8.15.0-alpine` and not `8.15.0` or `8.15.0-stretch`.
-
-Similarly, a user with `8.14` expects to be upgraded to `8.15` and not `8.15.0`.
-
-## What type of versioning is used?
+**What type of versioning is used?**
 
 It's pretty "wild west" for tagging and not always compliant with semver. Docker versioning in Renovate should do a best effort to accept and sort semver-like versions.
 
-## Are ranges supported? How?
-
-Yes and no. In theory, a tag of `8.15` should be like `>=8.15.0 <8.16.0`. In practice, there is nothing that enforces that the latest `8.15` code matches exactly with the latest `8.15.x` tag. e.g. `8.15` may not be the same as `8.15.0`, but it usually should be. Also, `8.15` is technically a version and not a range.
-
-## Range Strategy support
-
-We may be able to "pin" from range-like tags like `8.15` to semver-like tags like `8.15.0` but we should probably work out a way to compare the sha256 hashes before pinning.
-
-Otherwise, we can support "replace" strategy.
-
-"Bump" strategy and "extend" strategies are not applicable.
-
-## Implementation plan/status
+**Are ranges supported?**
 
-- [x] Compatibility check between tag suffixes
-- [x] Upgrade semver tags (e.g. `8.14.0` to `8.15.0`)
-- [x] Upgrade range-like tags (e.g. `8.14` to `8.15`)
-- [x] Allow non-semver-compatible leading zeroes, e.g. `16.04.0` to `16.04.1`
-- [x] Support getDigest for pinning and updating tag digests
+No. Although a tag like `12.15` might seem like it means `12.15.x`, it is a tag of its own and may or may not point to an of the available `12.15.x` tags, including `12.15.0`.
diff --git a/lib/versioning/git/index.ts b/lib/versioning/git/index.ts
index b545fc8a44..acef8725a4 100644
--- a/lib/versioning/git/index.ts
+++ b/lib/versioning/git/index.ts
@@ -1,6 +1,10 @@
 import * as generic from '../loose/generic';
 import { VersioningApi } from '../common';
 
+export const displayName = 'git';
+export const urls = ['https://git-scm.com/'];
+export const supportsRanges = false;
+
 const parse = (version: string): any => ({ release: [parseInt(version, 10)] });
 
 const isCompatible = (version: string, range: string): boolean => true;
diff --git a/lib/versioning/git/readme.md b/lib/versioning/git/readme.md
new file mode 100644
index 0000000000..4687ea83c9
--- /dev/null
+++ b/lib/versioning/git/readme.md
@@ -0,0 +1 @@
+Renovate's git versioning is a kind of hack to support git submodule updating.
diff --git a/lib/versioning/hashicorp/index.ts b/lib/versioning/hashicorp/index.ts
index b60f42d8cc..31f28d683f 100644
--- a/lib/versioning/hashicorp/index.ts
+++ b/lib/versioning/hashicorp/index.ts
@@ -1,6 +1,13 @@
 import { api as npm } from '../npm';
 import { VersioningApi, NewValueConfig } from '../common';
 
+export const displayName = 'Hashicorp';
+export const urls = [
+  'https://www.terraform.io/docs/configuration/terraform.html#specifying-a-required-terraform-version',
+];
+export const supportsRanges = true;
+export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
+
 function hashicorp2npm(input: string): string {
   // The only case incompatible with semver is a "short" ~>, e.g. ~> 1.2
   return input.replace(/~>(\s*\d+\.\d+$)/, '^$1').replace(',', '');
diff --git a/lib/versioning/hashicorp/readme.md b/lib/versioning/hashicorp/readme.md
index cb1f1e5603..238c5626b9 100644
--- a/lib/versioning/hashicorp/readme.md
+++ b/lib/versioning/hashicorp/readme.md
@@ -1,25 +1,3 @@
-# Hashicorp versioning
+Hashicorp versioning syntax is used for Terraform.
 
-## Documentation and URLs
-
-https://www.terraform.io/docs/configuration/terraform.html#specifying-a-required-terraform-version
-
-This versioning syntax is used for Terraform only currently.
-
-## What type of versioning is used?
-
-Hashicorp uses [Semantic Versioning 2.0](https://semver.org).
-
-## Are ranges supported? How?
-
-Hashicorp supports a subset of npm's range syntax.
-
-## Range Strategy support
-
-Hashicorp versioning should support all range strategies - pin, replace, bump, extend.
-
-## Implementation plan/status
-
-- [x] Add hashicorp2npm functions to leverage existing npm semver logic
-- [x] Exact version support
-- [x] Range support
+It is based off [Semantic Versioning 2.0](https://semver.org) but with a subset of npm's range syntax.
diff --git a/lib/versioning/hex/index.ts b/lib/versioning/hex/index.ts
index b7a7894ee9..a20d3c80cc 100644
--- a/lib/versioning/hex/index.ts
+++ b/lib/versioning/hex/index.ts
@@ -1,6 +1,11 @@
 import { api as npm } from '../npm';
 import { VersioningApi, NewValueConfig } from '../common';
 
+export const displayName = 'Hex';
+export const urls = ['https://hexdocs.pm/elixir/Version.html'];
+export const supportsRanges = true;
+export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
+
 function hex2npm(input: string): string {
   return input
     .replace(/~>\s*(\d+\.\d+)$/, '^$1')
diff --git a/lib/versioning/hex/readme.md b/lib/versioning/hex/readme.md
index 23ee8f6eba..154efd1f37 100644
--- a/lib/versioning/hex/readme.md
+++ b/lib/versioning/hex/readme.md
@@ -1,25 +1 @@
-# Hex versioning
-
-## Documentation and URLs
-
-https://hexdocs.pm/elixir/Version.html#module-requirements
-
-This versioning syntax is used for Elixir and Erlang hex dependencies
-
-## What type of versioning is used?
-
-Hex versions are based on [Semantic Versioning 2.0](https://semver.org)
-
-## Are ranges supported? How?
-
-Hex supports a [subset of npm range syntax](https://hexdocs.pm/elixir/Version.html).
-
-## Range Strategy support
-
-Hex versioning should support all range strategies - pin, replace, bump, extend.
-
-## Implementation plan/status
-
-- [x] Add hex2npm functions to leverage existing npm semver logic
-- [x] Exact version support
-- [x] Range support
+Hex versioning syntax is used for Elixir and Erlang hex dependencies. It is based on [Semantic Versioning 2.0](https://semver.org) and supports a subset of npm's range syntax.
diff --git a/lib/versioning/ivy/index.ts b/lib/versioning/ivy/index.ts
index 7f273001e6..73efe931d3 100644
--- a/lib/versioning/ivy/index.ts
+++ b/lib/versioning/ivy/index.ts
@@ -7,6 +7,11 @@ import {
 } from './parse';
 import { VersioningApi } from '../common';
 
+export const displayName = 'Ivy';
+export const urls = ['https://ant.apache.org/ivy/'];
+export const supportsRanges = true;
+export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
+
 const {
   equals,
   getMajor,
diff --git a/lib/versioning/loose/index.ts b/lib/versioning/loose/index.ts
index 2f2cf8b0c9..8304481a55 100644
--- a/lib/versioning/loose/index.ts
+++ b/lib/versioning/loose/index.ts
@@ -1,6 +1,10 @@
 import * as generic from './generic';
 import { VersioningApi } from '../common';
 
+export const displayName = 'Loose';
+export const urls = [];
+export const supportsRanges = false;
+
 const pattern = /^v?(\d+(?:\.\d+)*)(.*)$/;
 
 function parse(version: string): any {
diff --git a/lib/versioning/loose/readme.md b/lib/versioning/loose/readme.md
index 571a8c4fa7..c8620b9a90 100644
--- a/lib/versioning/loose/readme.md
+++ b/lib/versioning/loose/readme.md
@@ -1,23 +1 @@
-# loose versioning
-
-This "loose" is specific for Renovate and created for instances where no strict versioning is in place. It works like semver if semver-compliant versions are suppolied, but otherwise is "best effort".
-
-## Documentation and URLs
-
-None
-
-## What type of versioning is used?
-
-None, but it's semver-like.
-
-## Are ranges supported? How?
-
-No
-
-## Range Strategy support
-
-No
-
-## Implementation plan/status
-
-- [x] Best effort parsing and sorting
+Renovate's "loose" versioning was created for cases where no strict versioning is in place. It works like semver if semver-compliant versions are supplied, but otherwise is "best effort". Essentially it just does its best to sort versions.
diff --git a/lib/versioning/maven/index.ts b/lib/versioning/maven/index.ts
index c1636d7f76..8ea8d8148d 100644
--- a/lib/versioning/maven/index.ts
+++ b/lib/versioning/maven/index.ts
@@ -14,6 +14,15 @@ import {
 } from './compare';
 import { NewValueConfig, VersioningApi } from '../common';
 
+export const displayName = 'Maven';
+export const urls = [
+  'https://maven.apache.org/pom.html#Dependency_Version_Requirement_Specification',
+  'https://octopus.com/blog/maven-versioning-explained',
+  'https://maven.apache.org/enforcer/enforcer-rules/versionRanges.html',
+];
+export const supportsRanges = true;
+export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
+
 const equals = (a: string, b: string): boolean => compare(a, b) === 0;
 
 function matches(a: string, b: string): boolean {
diff --git a/lib/versioning/maven/readme.md b/lib/versioning/maven/readme.md
index 0ffb3427f4..a6d1c33d46 100644
--- a/lib/versioning/maven/readme.md
+++ b/lib/versioning/maven/readme.md
@@ -1,24 +1,3 @@
-# Maven versioning
+Maven versioning is similar to semver but also very different in places. It's specified by Maven itself.
 
-## Documentation and URLs
-
-https://maven.apache.org/pom.html#Dependency_Version_Requirement_Specification
-https://octopus.com/blog/maven-versioning-explained
-https://maven.apache.org/enforcer/enforcer-rules/versionRanges.html
-
-## What type of versioning is used?
-
-Maven's versioning is similar to semver but also very different in places. It's specified by Maven itself.
-
-## Are ranges supported? How?
-
-Yes, Maven uses its own special syntax for ranges.
-
-## Range Strategy support
-
-npm versioning should support all range strategies - pin, replace, bump, extend.
-
-## Implementation plan/status
-
-- [x] Exact version support
-- [ ] Range support (#2986)
+Ranges are supported using Maven's special syntax.
diff --git a/lib/versioning/node/index.ts b/lib/versioning/node/index.ts
index c7ac9e9a52..028427786a 100644
--- a/lib/versioning/node/index.ts
+++ b/lib/versioning/node/index.ts
@@ -1,6 +1,10 @@
 import npm, { isVersion, isValid } from '../npm';
 import { NewValueConfig, VersioningApi } from '../common';
 
+export const displayName = 'Node.js';
+export const urls = [];
+export const supportsRanges = false;
+
 function getNewValue({
   currentValue,
   rangeStrategy,
diff --git a/lib/versioning/node/readme.md b/lib/versioning/node/readme.md
index 03a68d8f03..b53137c719 100644
--- a/lib/versioning/node/readme.md
+++ b/lib/versioning/node/readme.md
@@ -1,24 +1,3 @@
-# node versioning
+Renovate's Node.js versioning is a wrapper around npm's versioning, except that it makes sure to strip "v" prefixes from exact versions when replacing.
 
-This "node" versioning is nearly identical to npm's semver except that it makes sure to strip "v" prefixes from exact versions when replacing.
-
-## Documentation and URLs
-
-https://semver.org/
-
-## What type of versioning is used?
-
-Node.JS's versioning complies with [Semantic Versioning 2.0](https://semver.org).
-
-## Are ranges supported? How?
-
-Same as npm.
-
-## Range Strategy support
-
-Same as npm
-
-## Implementation plan/status
-
-- [x] Strip v prefix
-- [ ] Support Node.js-specific "stable" awareness
+It is planned to extend it one day to support "stability" awareness, because Node.js's version stability does not follow the SemVer approach.
diff --git a/lib/versioning/npm/index.ts b/lib/versioning/npm/index.ts
index f78579d9f1..06a52c3061 100644
--- a/lib/versioning/npm/index.ts
+++ b/lib/versioning/npm/index.ts
@@ -3,6 +3,16 @@ import { is as isStable } from 'semver-stable';
 import { getNewValue } from './range';
 import { VersioningApi } from '../common';
 
+export const displayName = 'npm';
+export const urls = [
+  'https://semver.org/',
+  'https://www.npmjs.com/package/semver',
+  'https://docs.npmjs.com/about-semantic-versioning',
+  'https://semver.npmjs.com/',
+];
+export const supportsRanges = true;
+export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
+
 const {
   compare: sortVersions,
   maxSatisfying: maxSatisfyingVersion,
diff --git a/lib/versioning/npm/readme.md b/lib/versioning/npm/readme.md
index 29abebfc71..2a65882fed 100644
--- a/lib/versioning/npm/readme.md
+++ b/lib/versioning/npm/readme.md
@@ -1,25 +1,3 @@
-# npm versioning
+npm versioning is the most well known/widely-used implementation of [Semantic Versioning 2.0](https://semver.org).
 
-## Documentation and URLs
-
-https://semver.org/
-https://www.npmjs.com/package/semver
-https://docs.npmjs.com/about-semantic-versioning
-https://semver.npmjs.com/
-
-## What type of versioning is used?
-
-npm has the most well known/widely used implementation of [Semantic Versioning 2.0](https://semver.org).
-
-## Are ranges supported? How?
-
-npm's semver implementation supports a large number of range operators.
-
-## Range Strategy support
-
-npm versioning should support all range strategies - pin, replace, bump, extend.
-
-## Implementation plan/status
-
-- [x] Exact version support
-- [x] Range support
+It's important to understand that "npm" versioning scheme is not the same as "semver" versioning. SemVer's spec does not define ranges at all - so all range/constraint syntax in npm is npm-specific and not part of the spec.
diff --git a/lib/versioning/nuget/index.ts b/lib/versioning/nuget/index.ts
index 66a420fdd3..4769592cdb 100644
--- a/lib/versioning/nuget/index.ts
+++ b/lib/versioning/nuget/index.ts
@@ -1,6 +1,12 @@
 import * as generic from '../loose/generic';
 import { VersioningApi } from '../common';
 
+export const displayName = 'NuGet';
+export const urls = [
+  'https://docs.microsoft.com/en-us/nuget/concepts/package-versioning',
+];
+export const supportsRanges = false;
+
 const pattern = /^(\d+(?:\.\d+)*)(-[^+]+)?(\+.*)?$/;
 
 function parse(version: string): any {
diff --git a/lib/versioning/nuget/readme.md b/lib/versioning/nuget/readme.md
index 7ac1594e81..4e680039cc 100644
--- a/lib/versioning/nuget/readme.md
+++ b/lib/versioning/nuget/readme.md
@@ -1,24 +1,5 @@
-# NuGet versioning
-
-## Documentation and URLs
-
-https://docs.microsoft.com/en-us/nuget/concepts/package-versioning
-
-The intention of this version scheme is to match as closely as possible the version comparison that NuGet itself uses.
-
-## What type of versioning is used?
+NuGet versioning matches as closely as possible to the version comparison that NuGet itself uses.
 
 NuGet supports SemVer 2.0.0, but permits versions with differing numbers of version parts.
 
-## Are ranges supported? How?
-
 Ranges are not yet supported by this version scheme, but they are defined in NuGet and could be supported in the future.
-
-## Range Strategy support
-
-Not yet implemented.
-
-## Implementation plan/status
-
-- [x] Best effort parsing and sorting
-- [ ] Range support
diff --git a/lib/versioning/pep440/index.ts b/lib/versioning/pep440/index.ts
index f0bf0b09f1..e7b700663f 100644
--- a/lib/versioning/pep440/index.ts
+++ b/lib/versioning/pep440/index.ts
@@ -3,6 +3,11 @@ import { filter } from '@renovate/pep440/lib/specifier';
 import { getNewValue } from './range';
 import { VersioningApi } from '../common';
 
+export const displayName = 'PEP440';
+export const urls = ['https://www.python.org/dev/peps/pep-0440/'];
+export const supportsRanges = true;
+export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
+
 const {
   compare: sortVersions,
   satisfies: matches,
diff --git a/lib/versioning/pep440/readme.md b/lib/versioning/pep440/readme.md
index 04e96b5af1..faf4822d01 100644
--- a/lib/versioning/pep440/readme.md
+++ b/lib/versioning/pep440/readme.md
@@ -1,22 +1,3 @@
-# PEP 440 versioning
+PEP 440 is defined as part of the Python project, and its versioning is independent of others such as SemVer.
 
-## Documentation and URLs
-
-https://www.python.org/dev/peps/pep-0440/
-
-## What type of versioning is used?
-
-PEP 440 is part of the Python project, and its versioning is independent of others such as semver.
-
-## Are ranges supported? How?
-
-Ranges are supported, with a proprietary syntax.
-
-## Range Strategy support
-
-PEP 440 versioning should support all range strategies - pin, replace, bump, extend.
-
-## Implementation plan/status
-
-- [x] Exact version support
-- [x] Range support
+Ranges are supported using the syntax defined as part of the PEP440 spec.
diff --git a/lib/versioning/poetry/index.ts b/lib/versioning/poetry/index.ts
index e6b77e9cdd..3c976a54fb 100644
--- a/lib/versioning/poetry/index.ts
+++ b/lib/versioning/poetry/index.ts
@@ -3,6 +3,11 @@ import { major, minor } from 'semver';
 import { api as npm } from '../npm';
 import { NewValueConfig, VersioningApi } from '../common';
 
+export const displayName = 'Poetry';
+export const urls = ['https://python-poetry.org/docs/versions/'];
+export const supportsRanges = true;
+export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
+
 function notEmpty(s: string): boolean {
   return s !== '';
 }
diff --git a/lib/versioning/poetry/readme.md b/lib/versioning/poetry/readme.md
index b51b84e3d4..efbeccdcbf 100644
--- a/lib/versioning/poetry/readme.md
+++ b/lib/versioning/poetry/readme.md
@@ -1,27 +1,3 @@
-# Poetry versioning
+Poetry versioning is a little like a mix of PEP440 and SemVer.
 
-## Documentation and URLs
-
-https://poetry.eustace.io/docs/versions/
-
-## What type of versioning is used?
-
-Poetry uses [Semantic Versioning 2.0](https://semver.org).
-
-## Are ranges supported? How?
-
-Poetry supports ranges in a similar manner to npm, but not identical. The important differences are:
-
-##### Use of commas
-
-Multiple version requirements can be separated with a comma, e.g. `>= 1.2, < 1.5`. We interpret this to mean AND.
-
-## Range Strategy support
-
-Poetry versioning should support all range strategies - pin, replace, bump, extend.
-
-## Implementation plan/status
-
-- [x] Add poetry2npm and npm2poetry functions to leverage existing npm semver logic
-- [x] Exact version support
-- [x] Range support
+Currently Renovate's implementation is based off npm versioning, but it is being migrated to be based off PEP440 to be more compatible with Poetry's behaviour.
diff --git a/lib/versioning/regex/index.ts b/lib/versioning/regex/index.ts
index 78c69579b6..1c937e9edb 100644
--- a/lib/versioning/regex/index.ts
+++ b/lib/versioning/regex/index.ts
@@ -4,6 +4,10 @@ import { GenericVersion, GenericVersioningApi } from '../loose/generic';
 import { regEx } from '../../util/regex';
 import { CONFIG_VALIDATION } from '../../constants/error-messages';
 
+export const displayName = 'Regular Expression';
+export const urls = [];
+export const supportsRanges = false;
+
 export interface RegExpVersion extends GenericVersion {
   /** prereleases are treated in the standard semver manner, if present */
   prerelease: string;
diff --git a/lib/versioning/regex/readme.md b/lib/versioning/regex/readme.md
new file mode 100644
index 0000000000..6b44b52b35
--- /dev/null
+++ b/lib/versioning/regex/readme.md
@@ -0,0 +1,36 @@
+Regular Expression Versioning is designed to be like a flexible fallback versioning approach is Renovate's other versioning schemes don't do the job.
+
+The `regex` scheme makes use of Regular Express capture groups. The valid capture groups for `regex` versioning are:
+
+- `major`, `minor`, and `patch`: at least one of these must be provided. When determining whether a package has updated, these values will be compared in the standard semantic versioning fashion. If any of these fields are omitted, they will be treated as if they were `0` -- in this way, you can describe versioning schemes with up to three incrementing values.
+- `prerelease`: this value, if captured, will mark a given release as a prerelease (eg. unstable). If this value is captured and you have configured `"ignoreUnstable": true`, the given release will be skipped.
+- `compatibility`: this value defines the "build compatibility" of a given dependency. A proposed Renovate update will never change the specified compatibility value. For example, if you are pinning to `1.2.3-linux` (and `linux` is captured as the compatbility value), Renovate will not update you to `1.2.4-osx`.
+
+The compatibility concept was originally introduced for Docker versioning but sometimes package authors may use/misuse suffixes to mean compatibility in other versioning schemes.
+
+Here is an example of using `regex` versioning to correct behavior of the `guava` Maven package, which misuses suffixes as compatibility indicators:
+
+```json
+{
+  "packageRules": [
+    {
+      "packageNames": ["com.google.guava:guava"],
+      "versionScheme": "regex:^(?<major>\\d+)(\\.(?<minor>\\d+))?(\\.(?<patch>\\d+))?(-(?<compatibility>.*))?$"
+    }
+  ]
+}
+```
+
+Here is another example, this time for handling `python` Docker images, which use both pre-release indicators as well as version suffixes for compatibility:
+
+```json
+{
+  "packageRules": [
+    {
+      "datasources": ["docker"],
+      "packageNames": ["python"],
+      "versionScheme": "regex:^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(?<prerelease>[^.-]+)?(-(?<compatibility>.*))?$"
+    }
+  ]
+}
+```
diff --git a/lib/versioning/ruby/index.ts b/lib/versioning/ruby/index.ts
index 0d4b4b3f89..cb549aaca9 100644
--- a/lib/versioning/ruby/index.ts
+++ b/lib/versioning/ruby/index.ts
@@ -13,6 +13,15 @@ import { parse as parseRange, ltr } from './range';
 import { isSingleOperator, isValidOperator } from './operator';
 import { pin, bump, replace } from './strategies';
 
+export const displayName = 'Ruby';
+export const urls = [
+  'https://guides.rubygems.org/patterns/',
+  'https://bundler.io/v1.5/gemfile.html',
+  'https://www.devalot.com/articles/2012/04/gem-versions.html',
+];
+export const supportsRanges = true;
+export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
+
 function vtrim<T = unknown>(version: T): string | T {
   if (typeof version === 'string') return version.replace(/^v/, '');
   return version;
diff --git a/lib/versioning/ruby/readme.md b/lib/versioning/ruby/readme.md
index 3a33a0cb68..eca194c565 100644
--- a/lib/versioning/ruby/readme.md
+++ b/lib/versioning/ruby/readme.md
@@ -1,24 +1,3 @@
-# Ruby versioning
-
-## Documentation and URLs
-
-https://guides.rubygems.org/patterns/
-https://bundler.io/v1.5/gemfile.html
-https://www.devalot.com/articles/2012/04/gem-versions.html
-
-## What type of versioning is used?
-
-> The RubyGems team urges gem developers to follow the Semantic Versioning standard for their gem’s versions.
-
-## Are ranges supported? How?
+The RubyGems team urges gem developers to follow the Semantic Versioning standard for their gem’s versions, but it is not enforced.
 
 Range syntax is similar to npm's but not identical. The main difference is the use of "pessimistic" greater than or equals: `~>`
-
-## Range Strategy support
-
-Ruby versioning should support all range strategies - pin, replace, bump, extend.
-
-## Implementation plan/status
-
-- [x] Exact version support
-- [x] Range support
diff --git a/lib/versioning/semver/index.ts b/lib/versioning/semver/index.ts
index 389d81b4e5..c93d263dac 100644
--- a/lib/versioning/semver/index.ts
+++ b/lib/versioning/semver/index.ts
@@ -2,6 +2,10 @@ import semver from 'semver';
 import stable from 'semver-stable';
 import { NewValueConfig, VersioningApi } from '../common';
 
+export const displayName = 'Semantic';
+export const urls = ['https://semver.org/'];
+export const supportsRanges = false;
+
 const { is: isStable } = stable;
 
 const {
diff --git a/lib/versioning/semver/readme.md b/lib/versioning/semver/readme.md
index bd3388edc5..7b79556644 100644
--- a/lib/versioning/semver/readme.md
+++ b/lib/versioning/semver/readme.md
@@ -1,21 +1,3 @@
-# Semver versioning
+Renovate's Semantic Versioning is a strict/independent implementation of [Semantic Versioning 2.0](https://semver.org). It has been developed to be used in situations where exact-only semver support is needed and not npm's extended semver implementation including ranges.
 
-## Documentation and URLs
-
-https://semver.org/
-
-## What type of versioning is used?
-
-This is a strict/independent implementation of [Semantic Versioning 2.0](https://semver.org). It has been developed to be used in situations where exact-only semver support is needed and not npm's extended semver implementation including ranges.
-
-## Are ranges supported? How?
-
-Semver deliberately doesn't support any ranges, as per the spec
-
-## Range Strategy support
-
-Not applicable
-
-## Implementation plan/status
-
-- [x] Exact version support
+Ranges are not supported, as per the specification.
diff --git a/lib/versioning/swift/index.ts b/lib/versioning/swift/index.ts
index 61b583bb71..e7e0602215 100644
--- a/lib/versioning/swift/index.ts
+++ b/lib/versioning/swift/index.ts
@@ -3,6 +3,11 @@ import stable from 'semver-stable';
 import { toSemverRange, getNewValue } from './range';
 import { VersioningApi } from '../common';
 
+export const displayName = 'Swift';
+export const urls = ['https://swift.org/package-manager/'];
+export const supportsRanges = true;
+export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
+
 const { is: isStable } = stable;
 
 const {
diff --git a/lib/versioning/swift/readme.md b/lib/versioning/swift/readme.md
new file mode 100644
index 0000000000..9da29917c6
--- /dev/null
+++ b/lib/versioning/swift/readme.md
@@ -0,0 +1 @@
+Swift versioning was developed to support the Swift Package Manager. It's based on Semantic versioning but includes its own concept of ranges.
diff --git a/test/versioning/versioning-readmes.spec.ts b/test/versioning/versioning-readmes.spec.ts
index 4d7afafe33..35b0d717f9 100644
--- a/test/versioning/versioning-readmes.spec.ts
+++ b/test/versioning/versioning-readmes.spec.ts
@@ -1,11 +1,10 @@
 import { readdir, readFile } from 'fs-extra';
 
-describe('versioning readmes', () => {
-  it('has same questions for all version schemes', async () => {
+describe('versioning metadata', () => {
+  it('readme no markdown headers', async () => {
     const managers = (await readdir('lib/versioning')).filter(
       item => !item.includes('.')
     );
-    let expectedHeaders: string[];
     for (const manager of managers) {
       let readme: string;
       try {
@@ -17,11 +16,21 @@ describe('versioning readmes', () => {
         // ignore missing file
       }
       if (readme) {
-        const headers = readme
-          .match(/\n## (.*?)\n/g)
-          .map(match => match.substring(4, match.length - 1));
-        expectedHeaders = expectedHeaders || headers;
-        expect(headers).toEqual(expectedHeaders);
+        expect(RegExp(/(^|\n)#+ /).exec(readme)).toBe(null);
+      }
+    }
+  });
+  it('mandatory fields', async () => {
+    const managers = (await readdir('lib/versioning')).filter(
+      item => !item.includes('.')
+    );
+    for (const manager of managers) {
+      const managerObj = require(`../../lib/versioning/${manager}`);
+      expect(managerObj.displayName).toBeDefined();
+      expect(managerObj.urls).toBeDefined();
+      expect(managerObj.supportsRanges).toBeDefined();
+      if (managerObj.supportsRanges === true) {
+        expect(managerObj.supportedRangeStrategies).toBeDefined();
       }
     }
   });
-- 
GitLab