comver 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
comver/error.py ADDED
@@ -0,0 +1,82 @@
1
+ # SPDX-FileCopyrightText: © 2025 open-nudge <https://github.com/open-nudge>
2
+ # SPDX-FileContributor: szymonmaszke <github@maszke.co>
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ """Custom exceptions of `comver`."""
7
+
8
+ from __future__ import annotations
9
+
10
+
11
+ class ComverError(Exception):
12
+ """Base class for all exceptions raised by `comver`."""
13
+
14
+
15
+ class MessageUnrecognizedError(ComverError):
16
+ """Raised when the message is not recognized by any regexes."""
17
+
18
+ def __init__(self, message: str) -> None:
19
+ """Initialize the error.
20
+
21
+ Args:
22
+ message:
23
+ The message which was not recognized by any regexes.
24
+
25
+ """
26
+ self.message: str = message
27
+
28
+ super().__init__(
29
+ f"Message '{message}' is not recognized by any of the provided regexes."
30
+ )
31
+
32
+
33
+ class VersionFormatError(ComverError):
34
+ """Raised when the string version is not properly formatted.
35
+
36
+ Proper format should consist of three dot-separated elements,
37
+ e.g. `7.23.1`.
38
+
39
+ """
40
+
41
+ def __init__(self, version: str) -> None:
42
+ """Initialize the error.
43
+
44
+ Args:
45
+ version:
46
+ String version with incorrect formatting.
47
+
48
+ """
49
+ self.version: str = version
50
+
51
+ super().__init__(
52
+ f"Version should consist of three dot separated elements (MAJOR.MINOR.PATCH), got: {version}"
53
+ )
54
+
55
+
56
+ class VersionNotNumericError(ComverError):
57
+ """Raised when the string version contains non-numeric elements.
58
+
59
+ Proper format should consist of three dot-separated numeric elements,
60
+ e.g. `7.23.1`. This error is raised, if there is a version like
61
+ `1.2.0a1`.
62
+
63
+ Warning:
64
+ Only
65
+ [semantic Python versioning](https://packaging.python.org/en/latest/discussions/versioning/)
66
+ is allowed, e.g. no `1.2.0a1` for alpha releases
67
+
68
+ """
69
+
70
+ def __init__(self, version: str) -> None:
71
+ """Initialize the error.
72
+
73
+ Args:
74
+ version:
75
+ Properly formatted string version with non-numeric elements.
76
+
77
+ """
78
+ self.version: str = version
79
+
80
+ super().__init__(
81
+ f"One of the MAJOR, MINOR, PATCH is not an integer. Expected <INT>.<INT>.<INT>, got: {version}"
82
+ )
comver/plugin.py ADDED
@@ -0,0 +1,241 @@
1
+ # SPDX-FileCopyrightText: © 2025 open-nudge <https://github.com/open-nudge>
2
+ # SPDX-FileContributor: szymonmaszke <github@maszke.co>
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ """Extensions and plugins integrating `comver` with third party systems.
7
+
8
+ Most of the extensions are tailored to the Python ecosystem, and its
9
+ packagae managers specifically.
10
+
11
+ __Currently supported third party tools:__
12
+
13
+ - [`pdm`](https://pdm-project.org/en/latest/) package manager.
14
+ - [`hatch`](https://hatch.pypa.io/1.9/plugins/version-source/reference/)
15
+ package manager.
16
+
17
+ Warning:
18
+ Check out guidelines and tutorials for information about CLI/plugin
19
+ usage and suggested configuration. This section should be of interest
20
+ to people wanting to use the API directly (e.g. new integrations).
21
+
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import typing
27
+
28
+ from importlib.util import find_spec
29
+
30
+ from comver._version import Version, VersionCommit
31
+
32
+ if typing.TYPE_CHECKING:
33
+ import git
34
+
35
+ from comver.type_definitions import OptionalStringsOrPatterns
36
+
37
+
38
+ def pdm( # noqa: PLR0913
39
+ message_includes: OptionalStringsOrPatterns = None,
40
+ message_excludes: OptionalStringsOrPatterns = None,
41
+ path_includes: OptionalStringsOrPatterns = None,
42
+ path_excludes: OptionalStringsOrPatterns = None,
43
+ author_name_includes: OptionalStringsOrPatterns = None,
44
+ author_name_excludes: OptionalStringsOrPatterns = None,
45
+ author_email_includes: OptionalStringsOrPatterns = None,
46
+ author_email_excludes: OptionalStringsOrPatterns = None,
47
+ major_regexes: OptionalStringsOrPatterns = None,
48
+ minor_regexes: OptionalStringsOrPatterns = None,
49
+ patch_regexes: OptionalStringsOrPatterns = None,
50
+ unrecognized_message: typing.Literal["ignore", "error"] | None = None,
51
+ repository: str | git.Repo | None = None,
52
+ ) -> str:
53
+ """Entrypoint for `pdm`'s `[tool.pdm.version]` `pyproject.toml` specifier.
54
+
55
+ Example `pyproject.toml` usage:
56
+
57
+ ```toml
58
+ [build-system]
59
+ build-backend = "pdm.backend"
60
+
61
+ requires = [
62
+ "comver>=0.1.0",
63
+ "pdm-backend>=2",
64
+ ]
65
+
66
+ [tool.pdm.version]
67
+ source = "call"
68
+ getter = "commition.plugin.pdm:git"
69
+ ```
70
+
71
+ This function uses
72
+ [`Version.from_git_configured`][comver._version.Version.from_git_configured]
73
+ under the hood, __but only outputs the last yielded version `iterable`__.
74
+
75
+ Tip:
76
+ Plugin can be configured by `[tool.comver]` section.
77
+
78
+ Args:
79
+ message_includes:
80
+ Commit message regexes against which the commit is included.
81
+ Default: From config OR all paths are included.
82
+ message_excludes:
83
+ Commit message regexes against which the commit is excluded.
84
+ Default: From config OR no paths are excluded.
85
+ path_includes:
86
+ Path regexes against which the commit is included.
87
+ Default: From config OR all paths are included.
88
+ path_excludes:
89
+ Path regexes against which the commit is excluded.
90
+ Default: From config OR no paths are excluded.
91
+ author_name_includes:
92
+ Commit author names regexes against
93
+ which the commit is included.
94
+ Default: From config OR all names are included.
95
+ author_name_excludes:
96
+ Commit author names regexes against
97
+ which the commit is excluded.
98
+ Default: From config OR no names are excluded.
99
+ author_email_includes:
100
+ Commit author email regexes against
101
+ which the commit is included.
102
+ Default: From config OR all emails are included.
103
+ author_email_excludes:
104
+ Commit author email regexes against
105
+ which the commit is excluded.
106
+ Default: From config OR no emails are excluded.
107
+ major_regexes:
108
+ The regexes by which the major version is found.
109
+ Default: From config OR matches `feat!:` and `fix!:`
110
+ messages OR `BREAKING CHANGE` anywhere in the message.
111
+ minor_regexes:
112
+ The regex for the minor version.
113
+ Default: From config OR matches messages starting with `feat:`.
114
+ patch_regexes:
115
+ The regex for the patch version.
116
+ Default: From config OR matches messages starting with `fix:`.
117
+ unrecognized_message:
118
+ The behavior for unrecognized messages. It can be
119
+ either "exclude" or "error".
120
+ Default: From config OR "ignore"
121
+ repository:
122
+ The `git` repository.
123
+ Default: From config OR will be searched
124
+ in the parent directories.
125
+
126
+ Returns:
127
+ Calculated version as string (compatible with `pdm` interface).
128
+
129
+ """
130
+ version = VersionCommit()
131
+ for version in Version.from_git_configured( # noqa: B007
132
+ message_includes=message_includes,
133
+ message_excludes=message_excludes,
134
+ path_includes=path_includes,
135
+ path_excludes=path_excludes,
136
+ author_name_includes=author_name_includes,
137
+ author_name_excludes=author_name_excludes,
138
+ author_email_includes=author_email_includes,
139
+ author_email_excludes=author_email_excludes,
140
+ major_regexes=major_regexes,
141
+ minor_regexes=minor_regexes,
142
+ patch_regexes=patch_regexes,
143
+ unrecognized_message=unrecognized_message,
144
+ repository=repository,
145
+ ):
146
+ pass
147
+
148
+ return str(version.version)
149
+
150
+
151
+ if find_spec("hatchling"):
152
+ from hatchling.plugin import hookimpl
153
+ from hatchling.version.source.plugin.interface import VersionSourceInterface
154
+
155
+ class ComverVersionSource(VersionSourceInterface):
156
+ """Get the project version for the `hatchling` build backend.
157
+
158
+ This class implements the `VersionSourceInterface` from Hatchling
159
+ and allows integration of the `comver` plugin with Hatch-based
160
+ projects.
161
+
162
+ Tip:
163
+ See [plugins](https://hatch.pypa.io/1.13/plugins/version-source/reference/)
164
+ for more information
165
+
166
+ This function uses
167
+ [`Version.from_git_configured`][comver._version.Version.from_git_configured]
168
+ under the hood, __but only outputs the last version
169
+ yielded from `iterable`__.
170
+
171
+ > [!IMPORTANT]
172
+ > Plugin can also be configured by `[tool.hatch.version]`, not only
173
+ > `[tool.comver`]. The former takes precedence if both exist.
174
+
175
+ """
176
+
177
+ PLUGIN_NAME: str = "comver" # pyright: ignore [reportIncompatibleUnannotatedOverride]
178
+
179
+ def get_version_data(self) -> dict[str, str]: # pyright: ignore [reportImplicitOverride]
180
+ """Get the version data to be used by Hatchling.
181
+
182
+ Returns:
183
+ A dictionary with the resolved version string,
184
+ under the "version" key.
185
+ """
186
+ version = VersionCommit()
187
+ for version in Version.from_git_configured( # noqa: B007
188
+ message_includes=self.config.get("message_includes"), # pyright: ignore [reportUnknownArgumentType]
189
+ message_excludes=self.config.get("message_excludes"), # pyright: ignore [reportUnknownArgumentType]
190
+ path_includes=self.config.get("path_includes"), # pyright: ignore [reportUnknownArgumentType]
191
+ path_excludes=self.config.get("path_excludes"), # pyright: ignore [reportUnknownArgumentType]
192
+ author_name_includes=self.config.get("author_name_includes"), # pyright: ignore [reportUnknownArgumentType]
193
+ author_name_excludes=self.config.get("author_name_excludes"), # pyright: ignore [reportUnknownArgumentType]
194
+ author_email_includes=self.config.get("author_email_includes"), # pyright: ignore [reportUnknownArgumentType]
195
+ author_email_excludes=self.config.get("author_email_excludes"), # pyright: ignore [reportUnknownArgumentType]
196
+ repository=self.root,
197
+ ):
198
+ pass
199
+
200
+ return {"version": str(version.version)}
201
+
202
+ def set_version( # pyright: ignore [reportIncompatibleMethodOverride, reportImplicitOverride]
203
+ self,
204
+ _: str,
205
+ __: dict[str, str],
206
+ ) -> typing.NoReturn: # pragma: no cover
207
+ """Setting the version is not supported by the comver plugin.
208
+
209
+ This method exists to fulfill the interface, but always raises
210
+ `NotImplementedError`.
211
+
212
+ Args:
213
+ _:
214
+ The version string (ignored).
215
+ __:
216
+ Additional data (ignored).
217
+
218
+ Raises:
219
+ NotImplementedError:
220
+ Always raised to indicate that setting the
221
+ version is unsupported.
222
+ """
223
+ error = "comver plugin does not support setting the version."
224
+ raise NotImplementedError(error)
225
+
226
+ @hookimpl
227
+ def hatch_register_version_source() -> type[ComverVersionSource]:
228
+ """Automatically register hatchling plugin.
229
+
230
+ Note:
231
+ This function is called implicitly to register
232
+ `comver` for `hatchling` backend.
233
+
234
+ Returns:
235
+ ComverVersionSource class
236
+
237
+ """
238
+ return ComverVersionSource
239
+
240
+ else: # pragma: no cover
241
+ pass
comver/py.typed ADDED
File without changes
@@ -0,0 +1,4 @@
1
+ SPDX-FileCopyrightText: © 2025 open-nudge <https://github.com/open-nudge>
2
+ SPDX-FileContributor: szymonmaszke <github@maszke.co>
3
+
4
+ SPDX-License-Identifier: Apache-2.0
@@ -0,0 +1,27 @@
1
+ # SPDX-FileCopyrightText: © 2025 open-nudge <https://github.com/open-nudge>
2
+ # SPDX-FileContributor: szymonmaszke <github@maszke.co>
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ """Type definitions of `comver`.
7
+
8
+ Important:
9
+ This module is a simplification for `typing` of complex types
10
+ and should be used __eventually__ by third party plugin developers.
11
+
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import re
17
+
18
+ from collections.abc import Iterable
19
+
20
+ StringOrPattern = str | re.Pattern[str]
21
+ """Either `string` or compiled `re.Pattern`."""
22
+
23
+ StringsOrPatterns = Iterable[StringOrPattern]
24
+ """Iterable of `StringOrPattern`."""
25
+
26
+ OptionalStringsOrPatterns = Iterable[StringOrPattern] | None
27
+ """Iterable of `StringOrPattern` or `None`."""
@@ -0,0 +1,329 @@
1
+ Metadata-Version: 2.4
2
+ Name: comver
3
+ Version: 0.2.0
4
+ Summary: Commit-based semantic versioning - highly configurable and tag-free.
5
+ Author: open-nudge
6
+ Maintainer: open-nudge
7
+ License-Expression: Apache-2.0
8
+ Classifier: Development Status :: 2 - Pre-Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Typing :: Typed
18
+ Project-URL: Changelog, https://github.com/open-nudge/comver/blob/master/CHANGELOG.md
19
+ Project-URL: Documentation, https://open-nudge.github.io/comver
20
+ Project-URL: Homepage, https://open-nudge.github.io/comver
21
+ Project-URL: Issues, https://github.com/open-nudge/comver/issues
22
+ Project-URL: Repository, https://github.com/open-nudge/comver
23
+ Requires-Python: >=3.11
24
+ Requires-Dist: loadfig>=0.1.0
25
+ Requires-Dist: GitPython>=3
26
+ Provides-Extra: hatchling
27
+ Requires-Dist: hatchling>=1.27.0; extra == "hatchling"
28
+ Description-Content-Type: text/markdown
29
+
30
+ <!--
31
+ SPDX-FileCopyrightText: © 2025 open-nudge <https://github.com/open-nudge>
32
+ SPDX-FileContributor: szymonmaszke <github@maszke.co>
33
+
34
+ SPDX-License-Identifier: Apache-2.0
35
+ -->
36
+
37
+ # comver
38
+
39
+ <!-- mkdocs remove start -->
40
+
41
+ <!-- vale off -->
42
+
43
+ <!-- pyml disable-num-lines 30 line-length-->
44
+
45
+ <p align="center">
46
+ <em>Commit-based semantic versioning - highly configurable and tag-free.</em>
47
+ </p>
48
+
49
+ <div align="center">
50
+
51
+ <a href="https://pypi.org/project/comver">![PyPI - Python Version](https://img.shields.io/pypi/v/comver?style=for-the-badge&label=release&labelColor=grey&color=blue)
52
+ </a>
53
+ <a href="https://pypi.org/project/comver">![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fopen-nudge%2Fcomver%2Fmain%2Fpyproject.toml&style=for-the-badge&label=python&labelColor=grey&color=blue)
54
+ </a>
55
+ <a href="https://opensource.org/licenses/Apache-2.0">![License](https://img.shields.io/badge/License-Apache_2.0-blue?style=for-the-badge)
56
+ </a>
57
+ <a>![Coverage Hardcoded](https://img.shields.io/badge/coverage-100%25-green?style=for-the-badge)
58
+ </a>
59
+ <a href="https://scorecard.dev/viewer/?uri=github.com/open-nudge/comver">![OSSF-Scorecard Score](https://img.shields.io/ossf-scorecard/github.com/open-nudge/comver?style=for-the-badge&label=OSSF)
60
+ </a>
61
+
62
+ </div>
63
+
64
+ <p align="center">
65
+ ✨ <a href="#features">Features</a>
66
+ 🚀 <a href="#quick-start">Quick start</a>
67
+ 📚 <a href="https://open-nudge.github.io/comver">Documentation</a>
68
+ 🤝 <a href="#contribute">Contribute</a>
69
+ 👍 <a href="https://github.com/open-nudge/comver/blob/main/ADOPTERS.md">Adopters</a>
70
+ 📜 <a href="#legal">Legal</a>
71
+ </p>
72
+ <!-- vale on -->
73
+
74
+ ______________________________________________________________________
75
+
76
+ <!-- mkdocs remove end -->
77
+
78
+ ## Features
79
+
80
+ __comver__ is a tool for calculating __[semantic versioning](https://semver.org/)__
81
+ of your project __using only commit messages__ - no tags required!
82
+
83
+ - __Separation of concerns__: versioning focuses on technical aspects,
84
+ not marketing. You can now use tags solely for communication.
85
+ - __Highly configurable__: include only relevant commits by filtering via
86
+ `message`, `author`, `email`, __or even commit path__.
87
+ - __Immutable__: version is calculated directly from the commit history.
88
+ Tags can now be used more meaningfully (e.g., to mark a major milestone
89
+ or release).
90
+ - __Batteries-included__: integrate with [pdm](https://pdm-project.org/en/latest/),
91
+ [Hatch](https://hatch.pypa.io/latest/) or [uv](https://docs.astral.sh/uv/).
92
+ - __Verifiable__: verify that a specific version was generated from a
93
+ given commit chain - more resistant to tampering like
94
+ [dependency substitution attacks](https://docs.aws.amazon.com/codeartifact/latest/ug/dependency-substitution-attacks.html)
95
+
96
+ ## Why?
97
+
98
+ Semantic versioning based on Git tags has a few limitations:
99
+
100
+ - Teams may avoid bumping the `major` version due to the
101
+ perceived weight of the change.
102
+ [__Double versioning scheme__](https://open-nudge.github.io/comver/tutorials/why);
103
+ one version for technical changes, another for public releases is
104
+ a viable mitigation.
105
+ - Tag creation by `bot`s (e.g. during automated releases) lead to problems
106
+ with `branch protection` (see [here](https://github.com/orgs/community/discussions/25305)).
107
+ - Not all commits are relevant for release versions
108
+ (e.g., CI changes, bot updates, or tooling config),
109
+ yet many schemes count them in. With filtering, `comver` can exclude
110
+ such noise.
111
+ - Tags are mutable by default and can be re-pointed. By calculating the version
112
+ based on commits, and combining it with the commit
113
+ `sha` and a config `checksum`, you get verifiable and reproducible results.
114
+
115
+ ## Quick start
116
+
117
+ > [!NOTE]
118
+ > You can jump straight into the action and check `comver`
119
+ > [tutorials](https://open-nudge.github.io/comver/tutorials).
120
+
121
+ ### Installation
122
+
123
+ ```sh
124
+ > pip install comver
125
+ ```
126
+
127
+ ### Calculate version
128
+
129
+ > [!IMPORTANT]
130
+ > Although written in Python, comver can be used
131
+ > with any programming language.
132
+
133
+ If your commits follow the Conventional Commits format, run:
134
+
135
+ ```sh
136
+ > comver calculate
137
+ ```
138
+
139
+ This will output a version string in the `MAJOR.MINOR.PATCH` format:
140
+
141
+ ```sh
142
+ 23.1.3 # Output
143
+ ```
144
+
145
+ > [!IMPORTANT]
146
+ > You can use [plugins](https://open-nudge.github.io/comver/tutorials/plugins)
147
+ > to integrate this versioning scheme
148
+ > with [`pdm`](https://github.com/pdm-project/pdm) or
149
+ > [`hatch`](https://github.com/pypa/hatch). More below!
150
+
151
+ <!-- mkdocs remove start -->
152
+
153
+ ### Configuration
154
+
155
+ Configuration can be done either in `pyproject.toml`
156
+ (recommended for `Python`-first project) or in a separate
157
+ `.comver.toml` file (recommended for non-python projects):
158
+
159
+ <table>
160
+ <tr>
161
+ <th>pyproject.toml</th>
162
+ <th>.comver.toml</th>
163
+ </tr>
164
+ <tr>
165
+ <td>
166
+
167
+ ```toml
168
+ [tool.comver]
169
+ # Only commits to these paths are considered
170
+ path_includes = [
171
+ "src/*",
172
+ "pyproject.toml",
173
+ ]
174
+
175
+ # Commits done by GitHub Actions bot are discarded
176
+ author_name_excludes = [
177
+ "github-actions[bot]",
178
+ ]
179
+ ```
180
+
181
+ </td>
182
+ <td>
183
+
184
+ ```toml
185
+ # No [tool.comver] needed here
186
+ # Source only commits considered
187
+ path_includes = [
188
+ "src/*",
189
+ ]
190
+
191
+ # Commits messages with [no version] are discarded
192
+ message_excludes = [
193
+ ".*\[no version\].*",
194
+ ".*\[skipversion\].*",
195
+ ]
196
+ ```
197
+
198
+ </td>
199
+ </tr>
200
+ </table>
201
+
202
+ > [!TIP]
203
+ > See suggested configuration examples [here](https://open-nudge.github.io/comver/tutorials/configuration)
204
+
205
+ ### Integrations
206
+
207
+ > [!NOTE]
208
+ > You can use `comver` with [`uv`](https://github.com/astral-sh/uv)
209
+ > by selecting the appropriate [build backend](https://docs.astral.sh/uv/concepts/build-backend/#choosing-a-build-backend),
210
+ > such as `hatchling`.
211
+
212
+ To integrate `comver` with [`pdm`](https://pdm-project.org/en/latest/)
213
+ or [`hatch`](https://hatch.pypa.io/latest/) add the following to
214
+ your `pyproject.toml`:
215
+
216
+ <table>
217
+ <tr>
218
+ <th>PDM</th>
219
+ <th>Hatch</th>
220
+ </tr>
221
+ <tr>
222
+ <td>
223
+
224
+ ```toml
225
+ # Register comver for the build process
226
+ [build-system]
227
+ build-backend = "pdm.backend"
228
+
229
+ requires = [
230
+ "pdm-backend",
231
+ "comver>=0.1.0",
232
+ ]
233
+
234
+ # Setup versioning for PDM
235
+ [tool.pdm.version]
236
+ source = "call"
237
+ getter = "comver.plugin:pdm"
238
+
239
+ # Comver-specific settings
240
+ [tool.comver]
241
+ ...
242
+ ```
243
+
244
+ </td>
245
+ <td>
246
+
247
+ ```toml
248
+ # Register comver for the build process
249
+ [build-system]
250
+ build-backend = "hatchling.build"
251
+
252
+ requires = [
253
+ "comver>=0.1.0",
254
+ "hatchling",
255
+ ]
256
+
257
+ # Setup versioning for Hatchling
258
+ [tool.hatch.version]
259
+ source = "comver"
260
+
261
+
262
+ # Comver-specific settings
263
+ [tool.comver]
264
+ ...
265
+ ```
266
+
267
+ </td>
268
+ </tr>
269
+ </table>
270
+
271
+ > [!TIP]
272
+ > See more in the [documentation](https://open-nudge.github.io/comver/tutorials/plugins)
273
+
274
+ ### Verification
275
+
276
+ To verify that a version was produced from the same Git tree and configuration,
277
+ first use the calculate command with additional flags:
278
+
279
+ ```sh
280
+ comver calculate --sha --checksum
281
+ ```
282
+
283
+ This outputs three space-separated values:
284
+
285
+ ```sh
286
+ <VERSION> <SHA> <CHECKSUM>
287
+ ```
288
+
289
+ > [!TIP]
290
+ > Append `--format=json` for machine-friendly output
291
+
292
+ Before the next release provide these values to the `comver verify`
293
+ to ensure the version was previously generated from the
294
+ same codebase and config:
295
+
296
+ ```sh
297
+ comver verify <VERSION> <SHA> <CHECKSUM>
298
+ ```
299
+
300
+ If inconsistencies are found, you'll receive feedback, for example:
301
+
302
+ > Provided checksum and the checksum of configuration do not match.
303
+
304
+ > [!TIP]
305
+ > Explore verification workflows in the [tutorials](https://open-nudge.github.io/comver/tutorials/verification)
306
+
307
+ <!-- md-dead-link-check: off -->
308
+
309
+ ## Contribute
310
+
311
+ We welcome your contributions! Start here:
312
+
313
+ - [Code of Conduct](/CODE_OF_CONDUCT.md)
314
+ - [Contributing Guide](/CONTRIBUTING.md)
315
+ - [Roadmap](/ROADMAP.md)
316
+ - [Changelog](/CHANGELOG.md)
317
+ - [Report security vulnerabilities](/SECURITY.md)
318
+ - [Open an Issue](https://github.com/open-nudge/comver/issues)
319
+
320
+ ## Legal
321
+
322
+ - This project is licensed under the _Apache 2.0 License_ - see
323
+ the [LICENSE](/LICENSE.md) file for details.
324
+ - This project is copyrighted by _open-nudge_ - the
325
+ appropriate copyright notice is included in each file.
326
+
327
+ <!-- mkdocs remove end -->
328
+
329
+ <!-- md-dead-link-check: on -->