clq always validates the complete changelog, stopping at its first error. If a query is given, clq then queries the changelog and returns the query result. clq handles standard input — when no arguments are present or an argument is "-" — or any number of files.
clq exits with a status of 0 if all files are valid, and with a non-zero status if any file fails to validate. It writes to standard output the result of the query if a query was given.
clq writes validation error to standard error.
When processing multiple files, clq prefixes every line on standard out and standard error with the filename.
Usage: clq { options } <path to changelog.md>
Options are:
-changeMap name
name of a file defining the mapping from change kind to semantic version change
-output format
the format to apply to the result of a (complex) query. Supports json and md (markdown); defaults to json
-query string
A query to extract information out of the change log
-release
Enable release-mode validation
-with-filename
Always print filename headers with output lines
Example:
clq CHANGELOG.md
validates the file.clq -release CHANGELOG.md
validates the file and further enforces that the most recent release is neither [Unreleased] nor has been [YANKED]. This validation is recommended before cutting a release or merging to main.clq -query releases[0].version CHANGELOG.md
validates the complete changelog and returns the version of the most recent release.
A small minimal docker image offers a simple no-installation executable. This image’s label is the release version,
with a secondary label ending in -slim
, for example: 1.2.3-slim
.
A single changelog file can be validated with a simple docker run -i denisa/clq < CHANGELOG.md
.
To operate on multiple files is more complex and we recommend either multiple individual invocations, or the installation of native binaries.
Alternatively, the image is compatible with Whalebrew.
After a one time installation whalebrew install denisa/clq
, a one or more changelog files
can be validated with a simple clq CHANGELOG.md
or clq */CHANGELOG.md
.
The project also generates a 2nd docker image whose label ends wih -alpine
, for example: 1.2.3-alpine
.
This image, larger, is for use by the clq-action.
clq-action documents how to integrate clq in a GitHub workflow.
CHANGELOG = INTRODUCTION, RELEASES;
INTRODUCTION = TITLE, { ? markdown paragraph ? };
TITLE = "# ", ? inline content ?, LINE-ENDING;
RELEASES = [ UNRELEASED ], { RELEASED | YANKED };
UNRELEASED = UNRELEASED-HEAD, { CHANGES };
RELEASED = RELEASED-HEAD, { CHANGES };
YANKED = YANKED-HEAD, { CHANGES };
UNRELEASED-HEAD = "## [Unreleased]", LINE-ENDING;
RELEASED-HEAD = "## [", SEMVER, "] - ", ISO-DATE, [ LABEL ], LINE-ENDING;
LABEL = ? inline content, but not "[YANKED]" ?
YANKED-HEAD = "## ", SEMVER, " - ", ISO-DATE, " [YANKED]", LINE-ENDING;
CHANGES = CHANGE-KIND, { CHANGE-DESC };
CHANGE-KIND = "### ", ( "Added" | "Changed" | "Deprecated" | "Removed" | "Fixed" | "Security" ), LINE-ENDING;
CHANGE-DESC = "- ", ? inline content ?, LINE-ENDING;
SEMVER = ? see https://semver.org ?;
ISO-DATE = YEAR, "-", MONTH, "-" DAY;
YEAR = DIGIT, DIGIT, DIGIT, DIGIT;
MONTH = DIGIT, DIGIT;
DAY = DIGIT, DIGIT;
LINE-ENDING = "U+000A" | "U+000D" | "U+000DU+000A";
DIGIT = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
Note:
- The most recent version comes first.
- The only deviation from the official spec is the optional LABEL on a release entry.
The label is convenient for teams that want to highlight individual releases by naming
them. Such might be the case for releases cut, for example, for a quarterly demo:
## [1.5.2] - 2019.10.02 Espelho
clq supports two validation modes, feature and release. The feature mode is best used for feature branches: development work is in progress and the only requirement is for the changelog to be valid but not necessarily current. On the opposite, the release mode applies to release branches and, therefore, pull-requests.
By default, clq operates in the feature mode. In that mode, clq validates that the changelog file conforms to the grammar. It further validates that the releases are sorted chronologically from most recent to oldest, that the versions numbers are properly decreasing and that the version change between any two versions is justified by the change kinds present, according to a mapping from the type of change to the type of version change.
By default, the rules are:
- major release trigger:
Added
for new features.Removed
for now removed features.
- minor release trigger:
Changed
for changes in existing functionality.Deprecated
for soon-to-be removed features.
- bugfix release trigger:
Fixed
for any bugfixes.Security
in case of vulnerabilities.
The changeMap
option lets these rules be customized with a simple json file. In this example,
an Added section only triggers a minor version change:
[
{
"name": "Added",
"increment": "minor"
},
{
"name": "Changed",
"increment": "minor"
},
{
"name": "Deprecated",
"increment": "minor"
},
{
"name": "Fixed",
"increment": "patch"
},
{
"name": "Removed",
"increment": "major"
},
{
"name": "Security",
"increment": "patch"
}
]
clq is generally lenient with the spaces, accepting them between square brackets for example.
When the release mode is activated (with the -release
option), clq further validates
that the first entry in the changelog is an actual release entry.
Note that prereleases might or might not be supported at this time.
(Astérix & Obélix, Le tour de Gaule d’Astérix, 1953)
The changeMap
option further lets emoji be assigned to the change kinds with the optional emoji
attribute in the
json file. The example extend on the previous one and define emoji for each change kinds:
[
{
"name": "Added",
"increment": "minor",
"emoji": "✨"
},
{
"name": "Changed",
"increment": "major",
"emoji": "💥"
},
{
"name": "Deprecated",
"increment": "minor",
"emoji": "👎"
},
{
"name": "Fixed",
"increment": "patch",
"emoji": "🐛"
},
{
"name": "Removed",
"increment": "major",
"emoji": "🗑️"
},
{
"name": "Security",
"increment": "patch",
"emoji": "🔒"
}
]
It is possible to use the change map file to define other change kinds, be they translation of the standard one, or new ones. It is also possible to assign change kinds to the 'build' SemVer though those cannot be the single contribution of a release, as they would not increment the release number. Please see a French translation and using build for documentation.
A query is a sequence of query elements leading through the structure of the changelog to the desired field. The first query element is always a field from the changelog.
QUERY = ( SIMPLE_QUERY | COMPLEX_QUERY );
SIMPLE_QUERY = { ARRAY_FIELD, "." }, FIELD;
COMPLEX_QUERY = { ARRAY_FIELD, "." }, ARRAY_FIELD, ["/"];
ARRAY_FIELD = FIELD, "[", [SELECTOR], "]";
FIELD = ? see the Document Model section below ?;
SELECTOR = DIGIT+;
DIGIT = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
A simple query returns the value of a single field. It is not formatted.
A complex query returns all the values of the selected object.
The object is formatted according to the value of the -output
option.
If the query ends with a "/", it also returns the child elements.
If the selector is missing, the query returns a collection of objects.
For the sample changelog
# Change log
## [Unreleased]
### Added
- waldo
- fred
## [1.0.0] - 2020-06-20
### Removed
- foo
- bar
releases[1].version
->1.0.0
releases[1]
->{"version":"1.0.0", "date":"2020-06-20"}
releases[0].changes[]
->[{"title":"Added"}]
releases[0].changes[]/
->[{"title":"Added", "descriptions":["waldo", "fred"]}]
- releases[] all the releases defined in the changelog.
releases can be indexed, starting at 0, to access a single release. - title the title of the changelog
- changes[] all the changes for that release.
changes cannot be indexed. - date the release date, blank if it has not yet been released
- label the optional release label
- status one of prereleased, released, unreleased and yanked.
- title the version, date and optional label
- version the release version
- descriptions[] all the change descriptions;
descriptions cannot be indexed. - title, the change kind.