A command-line tool for developers who work with BO4E models. It builds on the BO4E-Schemas repository as single source of truth and helps you fetch, customise, compare and generate code from those schemas. It ships as a small self-contained binary with no runtime dependencies.
- Pull JSON schemas of a specific version (or
latest) from GitHub and replace the online$refs with relative paths so the schemas work offline. - Edit schemas via a static JSON config file — add fields, add models, add enum values, mark fields as non-nullable — to tailor the BO4E models to your use case.
- Generate code from the (edited) schemas. Currently supports
python-pydantic,python-sql-model,rust-plain, andrust-crateoutputs; further generators can be added. - Diff two schema directories and emit a machine-readable JSON diff.
- Compatibility matrix across a chain of diff files for quick visual review.
- Classify a version bump as technical / functional / major based on the diff.
- Repo versions — list version tags of the BO4E-python repository. Mostly used by CI.
- Graph the BO4E type-reference graph — emit a machine-readable GraphIR (JSON or GraphML), a big-picture diagram (DOT / PlantUML) with Louvain / component / package clustering, or per-class diagrams.
Pre-built binaries are published for macOS, Linux and Windows on every tag of the Releases page. Pick whichever channel is most convenient:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/bo4e/BO4E-CLI/releases/latest/download/bo4e-cli-installer.sh | shThe script installs the binary under ~/.local/bin (or $CARGO_HOME/bin if set) and
records an uninstall hook beside it.
irm https://github.com/bo4e/BO4E-CLI/releases/latest/download/bo4e-cli-installer.ps1 | iexDownload the latest bo4e-cli-x86_64-pc-windows-msvc.msi from the
Releases page and double-click it.
The CLI then appears under Apps & Features.
brew install bo4e/tap/bo4e-cliThe first run automatically taps bo4e/homebrew-tap (equivalent to
brew tap bo4e/tap && brew install bo4e-cli). Works on macOS (Intel and Apple
Silicon) and on x86_64 Linux via Homebrew on Linux.
Each release also ships raw tarballs (.tar.xz / .zip) for every supported target —
useful if you want to drop the binary somewhere yourself.
# compile from source:
cargo install bo4e-cli
# or fetch a pre-built binary without compiling:
cargo binstall bo4e-cliVerify the install:
bo4e --version
bo4e --helpScoop bucket distribution may be added later; until then Windows users should use the PowerShell or MSI installer above.
When installing from source you can pick a single generator instead of all of them:
cargo install bo4e-cli --no-default-features --features python-pydantic
cargo install bo4e-cli --no-default-features --features python-sql-model
cargo install bo4e-cli --no-default-features --features rust-plain
cargo install bo4e-cli --no-default-features --features rust-crate
Available selectors: python-pydantic, python-sql-model, rust-plain,
rust-crate, plus the umbrellas python (both Python flavours) and
rust (both Rust flavours).
The binary is removable in one step from every channel it shipped with:
| Installed via | Uninstall |
|---|---|
| Linux / macOS shell installer | run the uninstall script that the installer placed next to the binary, or simply rm $(which bo4e) |
| Windows PowerShell installer | re-run the installer URL with -Uninstall, or delete bo4e.exe from the install prefix shown by the installer |
| Windows MSI | Apps & Features → bo4e-cli → Uninstall |
| Homebrew (macOS / Linux) | brew uninstall bo4e-cli (optionally brew untap bo4e/tap) |
| Manual download | delete the binary you copied out of the tarball |
cargo install / binstall |
cargo uninstall bo4e-cli |
Shell completions are not installed automatically by any of the install
channels above (shell installer, MSI, PowerShell installer, manual tarball,
cargo install, cargo binstall). After installing or upgrading bo4e, run:
bo4e completions installto install (or refresh) completion for your current shell. The command auto-detects
your shell from $SHELL; pass --shell <SHELL> to override, and
bo4e completions uninstall to remove. Power users on system-managed paths can
pipe the script directly:
bo4e completions show zsh > /usr/share/zsh/site-functions/_bo4eSupported shells: bash, zsh, fish, powershell, elvish, nushell.
In the following I will describe some details on how to use the functionality provided by this CLI. Please keep in mind that I won't explain every command line option. In this regard, please refer to the help text of the CLI. If you are missing something in the following explanation and/or the help text, feel free to create an issue.
Note: The CLI has 3 output modes: normal, quiet and verbose. The normal mode is the default one and should be sufficient for most use cases. The quiet mode is useful if you want to use the CLI in a script and just want to check the exit code. The verbose mode is useful if you want to see more detailed information about the execution of the CLI. You can switch between these modes using the
--quietand--verboseflags. If you want to see the help text of a specific command, you can use the--helpflag after the command. For example,bo4e pull --helpwill show you the help text for thepullcommand.
Pull all BO4E-JSON-schemas of a specific version (or latest).
Beside the json-files a .version file will be created in utf-8 format at root of the output directory.
This file is needed for other commands of this CLI.
The schemas pulled from the repository BO4E-Schemas contain online references to each other
(e.g. "$ref": "https://raw.githubusercontent.com/BO4E/BO4E-Schemas/v202401.0.1/src/bo4e_schemas/bo/Angebot.json#").
This is not very convenient if you want to work with the schemas offline. And if you need to edit the schemas using
the config file, this would be a problem.
Per default (can be changed through command line option) the command replaces all online references with relative references.
Note: You might encounter rate limiting issues against the GitHub API. If true, please make use of a PAT. You can either provide it through the option
--tokenor by setting the environment variableGITHUB_ACCESS_TOKENor by having the GitHub CLI installed while being logged in. If a token wasn't provided either through the first or the second method, the CLI will automatically check for the GitHub CLI and if there is a user logged in. If true, the CLI will receive a temporary token by executinggh auth token.Also note that you don't need any special permissions behind this PAT. The GitHub API will increase the rate limit if the provided PAT is valid. If you are more interested in this, please refer to the GitHub documentation
Examples:
bo4e pull -o ./bo4e_schemas_latest # latest release
bo4e pull -o ./bo4e_schemas_v202501 -t v202501.0.0 # a specific tagOther pull flags: --no-update-refs (keep online $ref URLs as-is) and
--no-clear-output (skip wiping the output dir).
Tab completion on
-t / --version-tagfetches BO4E version tags from GitHub. Results are cached locally with a 60 s TTL and revalidated via conditional GET — see the flag's long-help text (bo4e pull --help) for details.
In short, this lets you edit the schemas using a static config-file. Ideally, no one should need it but in reality you might not have enough time to wait for the gremium or just want to experiment and elaborate an appropriate model. Here is a list of what it can do:
- Define non-nullable properties (in most cases changes it to a required field)
- Add additional properties
- Add additional models
- Add additional enum values
I think it's most effective to learn by example here:
{
"nonNullableFields": [
"bo\\.Angebot\\.angebotspreis",
"(bo|com)\\.\\w+\\._typ",
"\\w+\\.\\w+\\._id"
],
"additionalFields": [
{
"pattern": "bo\\.Angebot",
"fieldName": "foo",
"fieldDef": {
"type": "number"
}
},
{
"$ref": "./models/bo/Geschaeftspartner_extension.json"
}
],
"additionalEnumItems": [
{
"pattern": "enum\\.BoTyp",
"items": [
"Bilanzierung",
"Dokument"
]
}
],
"additionalModels": [
{
"module": "bo",
"schema": {
"$ref": "models/bo/Bilanzierung.json"
}
},
{
"module": "bo",
"schema": {
"additionalProperties": true,
"title": "Dokument",
"type": "object",
"description": "A generic document reference like for bills, order confirmations and cancellations",
"properties": {
"boTyp": {
"allOf": [
{
"$ref": "../enum/BoTyp.json#"
}
],
"title": "BoTyp",
"default": "Dokument"
},
"erstellungsdatum": {
"format": "date-time",
"title": "Erstellungsdatum",
"type": "string"
}
},
"required": [
"erstellungsdatum"
]
}
}
]
}The config file can contain the following keys:
nonNullableFields: A list of regex patterns which will be used to define non-nullable fields. The field will be required if the default value wasnull, which will be mostly the case. The regex pattern will be (full-)matched to the path of each field. An example of such a path would bebo.Angebot.angebotspreis. If the pattern matches, the field will be non-nullable.additionalFields: A list of additional fields which will be added to the schema.pattern: A regex pattern which will be used to match the path of the schema (e.g.bo.Angebot). The field will be added to each schema to which the pattern matches.fieldName: The name of the field which will be added.fieldDef: The definition of the field which will be added.
additionalEnumItems: A list of additional enum items which will be added to the schema.pattern: A regex pattern which will be used to match the path of the enum (e.g.enum.BoTyp). The items will be added to each enum to which the pattern matches.items: A list of items which will be added to the enum.
additionalModels: A list of additional models which will be added to the schema.module: The module to which the schema will be added.schema: The schema definition which will be added.
Note: For all config keys (except for nonNullableFields), you can alternatively use the "$ref" key
to reference to a file.
This is useful to keep the config file small and to reuse definitions.
If the path is relative it will be applied to the path of the directory where the config file is stored in.
But, you can define absolute paths if you want.
As a little extra feature for additionalFields: If you want to define multiple fields in one external file,
you can define a list of fields instead of a single field. The reference in the "$ref" key is the same.
Example of ./models/bo/Geschaeftspartner_extension.json:
[
{
"pattern": "bo\\.Geschaeftspartner",
"field_name": "foo",
"field_def": {
"type": "number"
}
},
{
"pattern": "bo\\.Geschaeftspartner",
"field_name": "bar",
"field_def": {
"type": "string"
}
}
]All BO4E-Schemas contain a field _version which holds the BO4E version. The schemas
pulled from BO4E-Schemas carry that as a
default; for additional models you define yourself this would otherwise be tedious to
keep in sync across version upgrades.
bo4e edit therefore rewrites the _version default on every schema to match the
.version file. Pass --no-default-version to opt out.
Edited output is "downstream" of the upstream BO4E release, so bo4e edit also brands
the output version with today's date as a .d<YYYYMMDD> suffix (e.g.
v202501.0.0.d20260511). This is on by default so the edited schemas are visibly
distinguishable from the unmodified release; pass --no-dirty-version to disable.
Example:
bo4e edit -i ./bo4e_schemas_latest -o ./bo4e_schemas_edited -c ./my_config_file.jsonOther edit flags: --no-clear-output (skip wiping the output dir before writing).
Generate code from BO4E JSON schemas. The output flavour is selected as a positional subcommand.
bo4e generate -i <input-dir> -o <output-dir> [--no-clear-output] [--templates-dir <dir>] <FLAVOUR> [flavour-options]
Examples:
# Python (pydantic-v2 models)
bo4e generate -i ./bo4e_schemas_edited -o ./bo4e_schemas_python python-pydantic
# Python (SQLModel — pydantic + SQLAlchemy ORM)
bo4e generate -i ./bo4e_schemas_edited -o ./bo4e_schemas_sql python-sql-model
# Rust (loose files for embedding into your own crate)
bo4e generate -i ./bo4e_schemas_edited -o ./src/bo4e rust-plain
# Rust (full Cargo crate with custom name)
bo4e generate -i ./bo4e_schemas_edited -o ./bo4e-rust-crate rust-crate --crate-name my-bo4eThe rust-plain flavour writes only .rs source files into the target directory and does not generate a Cargo.toml. The host crate must declare these dependencies for the generated code to compile (use rust-crate if you want them written for you):
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
chrono = { version = "0.4", features = ["serde"] }
uuid = { version = "1", features = ["serde", "v4", "macro-diagnostics"] }
rust_decimal = { version = "1", features = ["serde"] }
rust_decimal_macros = "1"| Argument | Short | Description |
|---|---|---|
--input |
-i |
Directory containing input JSON schemas. |
--output |
-o |
Directory to write generated code to. |
--no-clear-output |
Skip clearing the output directory before writing (default: clear). | |
--templates-dir |
Override embedded Jinja templates with a directory. | |
<FLAVOUR> |
One of python-pydantic, python-sql-model, rust-plain, rust-crate. |
|
--crate-name |
rust-crate only — Cargo package name written into the generated Cargo.toml (default: bo4e). |
Breaking change vs. earlier versions: the -t <flavour> flag has been replaced with a positional subcommand. Migrate by removing -t and moving the flavour name to the end of the command.
Compares the JSON-schemas in the two input directories and saves the differences to the output file (JSON). The output file will also contain information about the compared versions.
Here is an example of how this diff-file looks like
{
"old_schemas": {
"schemas": [
{
"module": [
"enum",
"Kundentyp"
],
"schema": {
// ...
}
}
// ...
],
"version": {
"major": 202501,
"functional": 0,
"technical": 0,
"candidate": null,
"commit_part": null,
"dirty_workdir_date": null
}
},
"new_schemas": {
"schemas": [
{
"module": [
"bo",
"Tarif"
],
"schema": {
// ...
}
}
// ...
],
"version": {
"major": 202501,
"functional": 1,
"technical": 0,
"candidate": null,
"commit_part": null,
"dirty_workdir_date": null
}
},
"changes": [
{
"type": "class_removed",
"old": "bo\\AdditionalModel.json",
"new": null,
"old_trace": "/bo/AdditionalModel#",
"new_trace": "/#"
},
{
"type": "field_type_changed",
"old": {
"description": "Eine generische ID, die für eigene Zwecke genutzt werden kann.\nZ.B. könnten hier UUIDs aus einer Datenbank stehen oder URLs zu einem Backend-System.",
"title": " Id",
"default": null,
"type": "string",
"format": null
},
"new": {
"description": "Eine generische ID, die für eigene Zwecke genutzt werden kann.\nZ.B. könnten hier UUIDs aus einer Datenbank stehen oder URLs zu einem Backend-System.",
"title": " Id",
"default": null,
"any_of": [
{
"description": "",
"title": "",
"default": null,
"type": "string",
"format": null
},
{
"description": "",
"title": "",
"default": null,
"type": "null"
}
]
},
"old_trace": "/com/Konzessionsabgabe#.properties['_id']",
"new_trace": "/com/Konzessionsabgabe#.properties['_id']"
}
// ...
]
}The type of change can be one of the following:
field_addedfield_removedfield_default_changedfield_description_changedfield_title_changed- field type change types:
field_cardinality_changedfield_reference_changedfield_string_format_changedfield_any_of_type_addedfield_any_of_type_removedfield_all_of_type_addedfield_all_of_type_removedfield_type_changed# An arbitrary unclassified change in type
class_addedclass_removedclass_description_changedenum_value_addedenum_value_removed
Example:
bo4e diff schemas ./bo4e_schemas_v2024.0.0 ./bo4e_schemas_latest -o diff_v2024.0.0_to_latest.jsonThis command creates a difference matrix just like the compatibility matrix visible in the documentation.
It uses multiple diff-files created by bo4e diff schemas where each file is represented by one column
in the resulting matrix. The diff-files will be ordered internally from earliest to latest version. So the order you
give the files as arguments doesn't matter. To make this work, the versions inside these diff files must be
consecutive and ascending. I.e. you have to be able to create an ascending series of versions where the new_version
must match the old_version of it's next neighbour. Example of valid input files:
| file 3 | → | file 1 | → | file 2 |
|---|---|---|---|---|
| v1.0.0 → v1.0.2 | v1.0.2 → v1.3.0 | v1.3.0 → v2.0.0 |
Example:
bo4e diff matrix diff_3.json diff_2.json diff_1.json -o matrix.csv -et csvGiven a diff file this command decides whether the list of changes corresponds to a functional or just technical change, compares that to the version numbers in the diff file, and reports whether the version bump is consistent.
Exit code: in --quiet mode the process exits with 1 on an invalid bump, so it
plugs straight into CI checks. In normal / verbose mode the failure is printed to
stderr but the process still exits 0 — interactive runs and shell pipelines that
don't care about the bump are not poisoned.
In --verbose mode the command additionally prints which bump kind it inferred from
the version numbers vs. from the list of changes, and notes when a major bump was
detected but suppressed via --no-major.
Example:
bo4e diff version-bump ./diff.json
bo4e diff version-bump ./diff.json --no-major # reject major bumps
bo4e --quiet diff version-bump ./diff.json # CI: exit code onlyLists the recent BO4E-python version tags reachable from a git reference, in descending chronological order. Run it inside a clone of bo4e/BO4E-python (technically any repo with the same versioning scheme works).
Flags:
| Flag | Short | Description |
|---|---|---|
-n |
Number of versions to retrieve. 0 (default) returns all versions since v202401.0.0. |
|
--ref |
-r |
Tag / branch / commit to start from (default: main). For a tag the tag itself is excluded. |
--exclude-candidates |
-c |
Skip release candidates. |
--exclude-technical-bumps |
-t |
Collapse technical-only versions to the latest one in each functional/major release. |
--show-full-commit-sha |
-s |
Show full commit SHA instead of the 6-character prefix. |
--no-validate-releases |
Skip checking that each tag has a published GitHub release (faster, fully offline). | |
--token |
GitHub PAT for the release-validation step (falls back to $GITHUB_ACCESS_TOKEN or gh auth token). |
Under --quiet only the version strings are printed (one per line) — handy for piping
into bo4e pull or bo4e diff. In normal mode a styled table with commit SHA and
date is rendered.
Example:
bo4e repo versions -n 5 --exclude-candidates --exclude-technical-bumpsBuild a machine-readable graph of all class-to-class references in a BO4E schemas
directory. The output is consumed by bo4e graph overview / bo4e graph single,
or you can export GraphML for external tools such as Gephi or yEd.
| Flag | Short | Description |
|---|---|---|
--input |
-i |
Schemas directory (typically the output of bo4e pull). |
--output |
-o |
Output file path. |
--format |
json (default — internal GraphIR consumed by overview/single) or graphml. |
Example:
bo4e graph extract -i ./bo4e_schemas_latest -o ./graph.jsonRender the big-picture diagram for every class in a graph file as Graphviz DOT or
PlantUML source. Nodes are package-coloured HTML tables; clusters can be Louvain
communities (default), weakly-connected components, BO4E packages
(bo, com, enum, …), or off entirely. Filters carve out a subset (glob
includes/excludes or BFS from a named class).
The DOT / PlantUML that this command writes is text — to turn it into an image
you either run Graphviz / PlantUML yourself (dot -Tsvg foo.dot > foo.svg,
plantuml -tsvg foo.puml, …) or pipe the source to Kroki,
an HTTP service that renders many diagram languages to SVG/PNG. Kroki has a
free public instance at https://kroki.io and ships as a Docker image, so the
lightest local setup is:
docker run -d --name kroki -p 8000:8000 yuzutech/krokiThe curl lines in the examples below then post the emitted source to
http://localhost:8000/<lang>/svg (where <lang> is graphviz or plantuml)
and write the rendered SVG to disk.
The default
--layout neato --overlap prismproduces the cleanest packing but needs a Graphviz built against the GTS triangulation library. The Graphviz binary bundled in the stockyuzutech/krokiimage is not linked against GTS, soprismsilently fails there. Either pass--overlap scale(works against any Graphviz build), or derive a Kroki image that installs the distro Graphviz over the bundled binary (apt install graphviz libgts-0.7-5) — the Ubuntu Graphviz package is built with GTS.
Example Dockerfile for a Kroki image with GTS-enabled Graphviz
# Overlay on top of the upstream Kroki image.
#
# Why this exists:
# Kroki ships a static `dot` binary from yuzutech/graphviz-builder that is
# compiled without GTS (the GNU Triangulated Surface library). That makes
# the following Graphviz features unusable:
# * `overlap=prism` / `overlap=false` (sfdp's overlap removal)
# * spline routing fallback on dense graphs (`dot` "lost edge" warnings)
# Ubuntu Noble's graphviz package is built --with-gts, so apt-installing it
# overlays /usr/bin/{dot,sfdp,neato,fdp,circo,twopi,…} with a GTS-enabled
# build. Kroki invokes the layout engines through standard PATH lookups,
# so this is transparent to the Kroki Java service.
FROM yuzutech/kroki:0.25.0
USER root
RUN apt-get update \
&& apt-get install -y --no-install-recommends graphviz libgts-0.7-5 \
&& apt-get clean && rm -rf /var/lib/apt/lists/*Build and run:
docker build -t kroki-gts -f Dockerfile.kroki .
docker run -d --name kroki -p 8000:8000 kroki-gtsExample 1 — full overview with per-class field-name labels:
bo4e graph extract -i ./bo4e_schemas_latest -o ./graph.json
bo4e graph overview -i ./graph.json -o ./overview.dot --detail names
curl --data-binary @overview.dot http://localhost:8000/graphviz/svg > overview.svgExample 2 — subset reachable from a single class (forward BFS), rendered with a
force-directed layout (--layout fdp) that packs the 22-node subgraph more
tightly than the default neato. --reachable-from accepts either a bare
class name (Vertrag) or a dotted module path (bo.Vertrag); a bare name
that matches more than one class across packages is rejected, pass the dotted
form to disambiguate. --node-margin 0 omits the sep attribute altogether
(the default sep="+50" is calibrated for the dense full overview and is
unnecessarily roomy on a small subset):
bo4e graph overview -i ./graph.json -o ./vertrag.dot --detail names \
--reachable-from Vertrag --layout fdp --node-margin 0
curl --data-binary @vertrag.dot http://localhost:8000/graphviz/svg > vertrag.svgFlags:
| Flag | Description |
|---|---|
--input, -i |
GraphIR JSON file produced by bo4e graph extract. |
--output, -o |
Output DOT / PlantUML source file. |
--format |
dot (default) or plantuml. |
--detail |
none / names / full — how much per-node detail to render. Default none. |
--clustering |
louvain (default), components, package, or none. |
--seed |
RNG seed for --clustering louvain (default: randomised each run; set for reproducible layouts). |
--include / --exclude |
Globs over dotted module paths (e.g. bo.*, *.Angebot). Repeatable; exclude is applied after include. |
--reachable-from |
Restrict to nodes reachable from this class via forward BFS. Accepts a bare name (Vertrag) or a dotted module path (bo.Vertrag); a bare name matching more than one class is rejected. |
--layout |
Graphviz engine: neato (default), dot, fdp, sfdp, circo, twopi. Ignored for --format plantuml. |
--overlap |
Overlap-removal strategy: prism (default; needs GTS), scale, scalexy, true, false. |
--node-margin |
Extra margin (points) around each node, for non-dot layouts. Default 50; pass 0 to fall back to Graphviz's +4. |
--edge-labels |
Re-enable fieldname [cardinality] labels on every edge (off by default to reduce clutter). |
--link-template |
URL template for clickable class nodes. Supports {pkg}, {module}, {class}, {namespace} (each with an optional .lower / .upper case accessor), {version} (no accessors), and the {cwd[.…]} / {output_dir[.…]} path variants. Run bo4e graph overview --help for the full reference. |
Render a focused diagram for a single class — or, with --class all, one diagram
per class in the graph. The output is a file in the first case and a directory
mirroring the BO4E package layout in the second. The focused class is drawn with
its fields; the neighbours within the chosen BFS radius are shown lighter.
Example (PlantUML output, rendered via the same local Kroki container as the overview examples above):
bo4e graph single -i ./graph.json -o ./geschaeftspartner.puml \
--class Geschaeftspartner --format plantuml
curl --data-binary @geschaeftspartner.puml http://localhost:8000/plantuml/svg \
> geschaeftspartner.svgFlags:
| Flag | Description |
|---|---|
--input, -i |
GraphIR JSON file. |
--output, -o |
File path (with --class <NAME>) or directory (with --class all). |
--class |
Bare name (Angebot), dotted path (bo.Angebot), or all. Default all. |
--format |
dot (default) or plantuml. |
--detail-root |
Detail level for the focused class: none / names / full. Default full. |
--detail-neighbours |
Detail level for neighbour classes. Default none. |
--clustering |
package (default) or none. louvain / components are rejected — the per-class ego graph is too small for them to be meaningful. |
--include / --exclude |
Globs over dotted module paths. Default scope keeps siblings in the same BO4E package. |
--radius |
BFS radius around the focused class (default 1). |
--link-template |
Same URL template engine as bo4e graph overview. |
--no-clear-output, -c |
Don't wipe the output directory before writing. Only relevant with --class all; for single-class targets the file is overwritten in place regardless. |
This repo is a Cargo workspace with three crates:
crates/bo4e-cli— thebo4ebinary, command dispatch, console, IO.crates/bo4e-schemas— typed JSON-Schema model and version handling.crates/bo4e-codegen— pluggable code generators (Python pydantic, Python SQLModel, Rust plain, Rust crate).
Common commands:
cargo build --workspace
cargo test --workspace
cargo clippy --workspace -- -D warnings
cargo fmt --allIssues and pull requests are very welcome — please open them against the main branch.
Conventional Commits (feat(...): …, fix(...): …, docs(...): …, etc.) are
required because the CHANGELOG.md is auto-generated from them. Commits whose
type is not user-facing (chore, ci, build, style, test) are skipped.
-
Go to Actions → Release: prepare → Run workflow and enter the next semver version (e.g.
0.2.0). -
The workflow bumps
Cargo.toml, regeneratesCHANGELOG.mdfrom the commits since the previous tag, and opens a pull request. -
Review the CHANGELOG diff, tweak any wording, then merge the PR.
-
From the merged commit, push the tag:
git tag v0.2.0 git push origin v0.2.0
That fires the release pipeline, which cross-compiles for macOS / Linux / Windows and publishes a GitHub Release with all installers and tarballs attached. The CHANGELOG section for that version is embedded in the release notes automatically.