diff --git a/.github/fixtures/test-bump-unreleased-with-tag-message-arg/cliff.toml b/.github/fixtures/test-bump-unreleased-with-tag-message-arg/cliff.toml new file mode 100644 index 00000000..9b43af3 --- /dev/null +++ b/.github/fixtures/test-bump-unreleased-with-tag-message-arg/cliff.toml @@ -0,0 +1,37 @@ +[changelog] +# template for the changelog footer +header = """ +# Changelog\n +All notable changes to this project will be documented in this file.\n +""" +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +{% if version %}\ + ## [{{ version | trim_start_matches(pat="v") }}] + {% if message %} + {{ message }} + {% endif %}\ +{% else %}\ + ## [unreleased] +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | upper_first }} + {% for commit in commits %} + - {{ commit.message | upper_first }}\ + {% endfor %} +{% endfor %}\n +""" +# template for the changelog footer +footer = """ + +""" +# remove the leading and trailing whitespace from the templates +trim = true + +[git] +# regex for parsing and grouping commits +commit_parsers = [ + { message = "^feat", group = "Features", default_scope = "app" }, + { message = "^fix", group = "Bug Fixes", scope = "cli" }, +] diff --git a/.github/fixtures/test-bump-unreleased-with-tag-message-arg/commit.sh b/.github/fixtures/test-bump-unreleased-with-tag-message-arg/commit.sh new file mode 100755 index 00000000..7c6fe32 --- /dev/null +++ b/.github/fixtures/test-bump-unreleased-with-tag-message-arg/commit.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -e + +GIT_COMMITTER_DATE="2022-04-06 01:25:08" git commit --allow-empty -m "Initial commit" +GIT_COMMITTER_DATE="2022-04-06 01:25:09" git commit --allow-empty -m "feat: add feature 1" +GIT_COMMITTER_DATE="2022-04-06 01:25:10" git commit --allow-empty -m "fix: fix feature 1" +git tag v0.1.0 +GIT_COMMITTER_DATE="2022-04-06 01:25:11" git commit --allow-empty -m "feat(gui): add feature 2" +GIT_COMMITTER_DATE="2022-04-06 01:25:12" git commit --allow-empty -m "fix(gui): fix feature 2" +git tag v0.2.0 +GIT_COMMITTER_DATE="2022-04-06 01:25:13" git commit --allow-empty -m "test: add tests" diff --git a/.github/fixtures/test-bump-unreleased-with-tag-message-arg/expected.md b/.github/fixtures/test-bump-unreleased-with-tag-message-arg/expected.md new file mode 100644 index 00000000..bcb74bd --- /dev/null +++ b/.github/fixtures/test-bump-unreleased-with-tag-message-arg/expected.md @@ -0,0 +1,13 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [0.2.1] + +Some text + +### Test + +- Add tests + + diff --git a/.github/fixtures/test-bump-version-custom-minor/cliff.toml b/.github/fixtures/test-bump-version-custom-minor/cliff.toml new file mode 100644 index 00000000..b759042 --- /dev/null +++ b/.github/fixtures/test-bump-version-custom-minor/cliff.toml @@ -0,0 +1,32 @@ +[changelog] +# template for the changelog footer +header = """ +# Changelog\n +All notable changes to this project will be documented in this file.\n +""" +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +{% if version %}\ + ## [{{ version | trim_start_matches(pat="v") }}] +{% else %}\ + ## [unreleased] +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | upper_first }} + {% for commit in commits %} + - {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\ + {% endfor %} +{% endfor %}\n +""" +# template for the changelog footer +footer = """ + +""" +# remove the leading and trailing whitespace from the templates +trim = true + +[bump] +features_always_bump_minor = true +breaking_always_bump_major = true +custom_minor_increment_regex = "minor|more" diff --git a/.github/fixtures/test-bump-version-custom-minor/commit.sh b/.github/fixtures/test-bump-version-custom-minor/commit.sh new file mode 100755 index 00000000..8d9f277 --- /dev/null +++ b/.github/fixtures/test-bump-version-custom-minor/commit.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -e + +GIT_COMMITTER_DATE="2021-01-23 01:23:45" git commit --allow-empty -m "feat: add feature 1" +GIT_COMMITTER_DATE="2021-01-23 01:23:45" git commit --allow-empty -m "feat: add feature 2" +git tag v0.1.0 + +GIT_COMMITTER_DATE="2021-01-23 01:23:46" git commit --allow-empty -m "minor: add minor" +GIT_COMMITTER_DATE="2021-01-23 01:23:46" git commit --allow-empty -m "fix: fix feature 2" diff --git a/.github/fixtures/test-bump-version-custom-minor/expected.md b/.github/fixtures/test-bump-version-custom-minor/expected.md new file mode 100644 index 00000000..6705546 --- /dev/null +++ b/.github/fixtures/test-bump-version-custom-minor/expected.md @@ -0,0 +1,22 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [0.2.0] + +### Fix + +- Fix feature 2 + +### Minor + +- Add minor + +## [0.1.0] + +### Feat + +- Add feature 1 +- Add feature 2 + + diff --git a/.github/fixtures/test-tag-message/cliff.toml b/.github/fixtures/test-tag-message/cliff.toml new file mode 100644 index 00000000..1f2be1c --- /dev/null +++ b/.github/fixtures/test-tag-message/cliff.toml @@ -0,0 +1,37 @@ +[changelog] +# template for the changelog footer +header = """ +# Changelog\n +All notable changes to this project will be documented in this file.\n +""" +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +{% if version %}\ + ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} + {% if message %} + {{ message }} + {% endif %}\ +{% else %}\ + ## [unreleased] +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | upper_first }} + {% for commit in commits %} + - {{ commit.message | upper_first }}\ + {% endfor %} +{% endfor %}\n +""" +# template for the changelog footer +footer = """ + +""" +# remove the leading and trailing whitespace from the templates +trim = true + +[git] +# regex for parsing and grouping commits +commit_parsers = [ + { message = "^feat", group = "Features", default_scope = "app" }, + { message = "^fix", group = "Bug Fixes", scope = "cli" }, +] diff --git a/.github/fixtures/test-tag-message/commit.sh b/.github/fixtures/test-tag-message/commit.sh new file mode 100755 index 00000000..f2ec2b9 --- /dev/null +++ b/.github/fixtures/test-tag-message/commit.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -e + +GIT_COMMITTER_DATE="2022-04-06 01:25:08" git commit --allow-empty -m "Initial commit" +GIT_COMMITTER_DATE="2022-04-06 01:25:09" git commit --allow-empty -m "feat: add feature 1" +GIT_COMMITTER_DATE="2022-04-06 01:25:10" git commit --allow-empty -m "fix: fix feature 1" +git tag v0.1.0 -m "Some text" +GIT_COMMITTER_DATE="2022-04-06 01:25:11" git commit --allow-empty -m "feat(gui): add feature 2" +GIT_COMMITTER_DATE="2022-04-06 01:25:12" git commit --allow-empty -m "fix(gui): fix feature 2" +git tag v0.2.0 +GIT_COMMITTER_DATE="2022-04-06 01:25:13" git commit --allow-empty -m "test: add tests" diff --git a/.github/fixtures/test-tag-message/expected.md b/.github/fixtures/test-tag-message/expected.md new file mode 100644 index 00000000..afcaca4 --- /dev/null +++ b/.github/fixtures/test-tag-message/expected.md @@ -0,0 +1,33 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [unreleased] + +### Test + +- Add tests + +## [0.2.0] - 2022-04-06 + +### Bug Fixes + +- Fix feature 2 + +### Features + +- Add feature 2 + +## [0.1.0] - 2022-04-06 + +Some text + +### Bug Fixes + +- Fix feature 1 + +### Features + +- Add feature 1 + + diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fbbd628..b00e6d7 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -69,7 +69,7 @@ jobs: - name: Build and push id: docker_build - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./Dockerfile diff --git a/.github/workflows/test-fixtures.yml b/.github/workflows/test-fixtures.yml index 0afbefd..77e30ca 100644 --- a/.github/workflows/test-fixtures.yml +++ b/.github/workflows/test-fixtures.yml @@ -38,6 +38,8 @@ jobs: - fixtures-name: test-split-commits - fixtures-name: test-bump-version command: --bump + - fixtures-name: test-bump-version-custom-minor + command: --bump - fixtures-name: test-bumped-version command: --bumped-version - fixtures-name: test-footer-template @@ -79,6 +81,9 @@ jobs: command: --bump --tag=2.1.1 - fixtures-name: test-cli-arg-ignore-tags command: --ignore-tags ".*beta" + - fixtures-name: test-tag-message + - fixtures-name: test-bump-unreleased-with-tag-message-arg + command: --bump --unreleased --with-tag-message "Some text" steps: - name: Checkout diff --git a/Cargo.lock b/Cargo.lock index 73250ee..e9113e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -315,9 +315,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.5" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2020fa13af48afc65a9a87335bda648309ab3d154cd03c7ff95b378c7ed39c4" +checksum = "fbca90c87c2a04da41e95d1856e8bcd22f159bdbfa147314d2ce5218057b0e58" dependencies = [ "clap", ] @@ -523,17 +523,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "displaydoc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.63", -] - [[package]] name = "dissimilar" version = "1.0.9" @@ -810,7 +799,7 @@ dependencies = [ "next_version", "pretty_assertions", "regex", - "reqwest 0.12.4", + "reqwest 0.12.5", "reqwest-middleware", "rust-embed", "secrecy", @@ -1006,7 +995,7 @@ dependencies = [ "http 1.1.0", "http-cache", "http-cache-semantics", - "reqwest 0.12.4", + "reqwest 0.12.5", "reqwest-middleware", "serde", "url", @@ -1120,19 +1109,20 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.26.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" dependencies = [ "futures-util", "http 1.1.0", "hyper 1.3.1", "hyper-util", - "rustls 0.22.4", + "rustls 0.23.10", "rustls-pki-types", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "tower-service", + "webpki-roots 0.26.1", ] [[package]] @@ -1178,134 +1168,14 @@ dependencies = [ "cc", ] -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.63", -] - [[package]] name = "idna" -version = "1.0.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "icu_normalizer", - "icu_properties", - "smallvec", - "utf8_iter", + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -1462,9 +1332,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" @@ -1544,12 +1414,6 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" -[[package]] -name = "litemap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" - [[package]] name = "litrs" version = "0.4.1" @@ -1634,11 +1498,12 @@ dependencies = [ [[package]] name = "next_version" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7beae5e84c3330a90f0f89eae10f5cd4c17c3be0f119ab36d94fd908c7b8c8fb" +checksum = "a0c2cdfd777d2aef50e83560295f35b11a934efdaae1eb92c807c6b4844b435b" dependencies = [ "conventional_commit_parser", + "regex", "semver", ] @@ -1889,6 +1754,53 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.10", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash", + "rustls 0.23.10", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.36" @@ -2007,7 +1919,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-rustls 0.24.1", @@ -2022,9 +1934,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ "async-compression", "base64 0.22.1", @@ -2035,7 +1947,7 @@ dependencies = [ "http-body 1.0.0", "http-body-util", "hyper 1.3.1", - "hyper-rustls 0.26.0", + "hyper-rustls 0.27.2", "hyper-util", "ipnet", "js-sys", @@ -2044,15 +1956,16 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.22.4", + "quinn", + "rustls 0.23.10", "rustls-pemfile 2.1.2", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.1", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "tokio-util", "tower-service", "url", @@ -2072,7 +1985,7 @@ dependencies = [ "anyhow", "async-trait", "http 1.1.0", - "reqwest 0.12.4", + "reqwest 0.12.5", "serde", "thiserror", "tower-service", @@ -2146,6 +2059,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" version = "0.38.34" @@ -2180,7 +2099,21 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki 0.102.3", + "rustls-webpki 0.102.4", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls" +version = "0.23.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.4", "subtle", "zeroize", ] @@ -2222,9 +2155,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.3" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ "ring", "rustls-pki-types", @@ -2440,12 +2373,6 @@ dependencies = [ "xxhash-rust", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "strsim" version = "0.11.1" @@ -2487,15 +2414,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] -name = "synstructure" -version = "0.13.1" +name = "sync_wrapper" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.63", -] +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] name = "system-configuration" @@ -2623,15 +2545,20 @@ dependencies = [ ] [[package]] -name = "tinystr" -version = "0.7.6" +name = "tinyvec" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ - "displaydoc", - "zerovec", + "tinyvec_macros", ] +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.38.0" @@ -2672,11 +2599,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.22.4", + "rustls 0.23.10", "rustls-pki-types", "tokio", ] @@ -2775,9 +2702,21 @@ checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -2864,12 +2803,27 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.12" @@ -2908,7 +2862,7 @@ dependencies = [ "once_cell", "rustls 0.22.4", "rustls-pki-types", - "rustls-webpki 0.102.3", + "rustls-webpki 0.102.4", "serde", "serde_json", "url", @@ -2917,9 +2871,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -2927,18 +2881,6 @@ dependencies = [ "serde", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "utf8parse" version = "0.2.1" @@ -3312,18 +3254,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - [[package]] name = "xxhash-rust" version = "0.8.10" @@ -3345,79 +3275,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" -[[package]] -name = "yoke" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.63", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.63", - "synstructure", -] - [[package]] name = "zeroize" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" -[[package]] -name = "zerovec" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.63", -] - [[package]] name = "zstd" version = "0.13.1" diff --git a/Cargo.toml b/Cargo.toml index 4647077..9f47692 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ regex = "1.10.5" glob = "0.3.1" log = "0.4.21" secrecy = { version = "0.8.0", features = ["serde"] } -lazy_static = "1.4.0" +lazy_static = "1.5.0" dirs = "5.0.1" [profile.dev] diff --git a/git-cliff-core/Cargo.toml b/git-cliff-core/Cargo.toml index 0dc7530..c7634d2 100644 --- a/git-cliff-core/Cargo.toml +++ b/git-cliff-core/Cargo.toml @@ -57,10 +57,10 @@ tera = "1.20.0" indexmap = { version = "2.2.6", optional = true } toml = "0.8.14" lazy-regex = "3.1.0" -next_version = "0.2.17" +next_version = "0.2.18" semver = "1.0.23" document-features = { version = "0.2.8", optional = true } -reqwest = { version = "0.12.4", default-features = false, features = [ +reqwest = { version = "0.12.5", default-features = false, features = [ "rustls-tls", "json", "zstd", @@ -72,7 +72,7 @@ tokio = { version = "1.38.0", features = [ "macros", ], optional = true } futures = { version = "0.3.30", optional = true } -url = "2.5.1" +url = "2.5.2" dyn-clone = "1.0.17" [dependencies.git2] diff --git a/git-cliff-core/src/changelog.rs b/git-cliff-core/src/changelog.rs index 1f1ed4b..c6a84d9 100644 --- a/git-cliff-core/src/changelog.rs +++ b/git-cliff-core/src/changelog.rs @@ -815,6 +815,7 @@ mod test { }; let test_release = Release { version: Some(String::from("v1.0.0")), + message: None, commits: vec![ Commit::new( String::from("coffee"), @@ -910,6 +911,7 @@ mod test { }, Release { version: None, + message: None, commits: vec![ Commit::new( String::from("abc123"), diff --git a/git-cliff-core/src/config.rs b/git-cliff-core/src/config.rs index 1a142ab..eea7392 100644 --- a/git-cliff-core/src/config.rs +++ b/git-cliff-core/src/config.rs @@ -200,6 +200,24 @@ pub struct Bump { /// /// When set, the version will be set to this value if no tags are found. pub initial_tag: Option, + + /// Configure a custom regex pattern for major version increments. + /// + /// This will check only the type of the commit against the given pattern. + /// + /// ### Note + /// + /// `commit type` according to the spec is only `[a-zA-Z]+` + pub custom_major_increment_regex: Option, + + /// Configure a custom regex pattern for minor version increments. + /// + /// This will check only the type of the commit against the given pattern. + /// + /// ### Note + /// + /// `commit type` according to the spec is only `[a-zA-Z]+` + pub custom_minor_increment_regex: Option, } /// Parser for grouping commits. diff --git a/git-cliff-core/src/lib.rs b/git-cliff-core/src/lib.rs index 6f78c91..812e0e7 100644 --- a/git-cliff-core/src/lib.rs +++ b/git-cliff-core/src/lib.rs @@ -33,6 +33,8 @@ pub mod remote; /// Git repository. #[cfg(feature = "repo")] pub mod repo; +/// Git tag. +pub mod tag; /// Template engine. pub mod template; diff --git a/git-cliff-core/src/release.rs b/git-cliff-core/src/release.rs index 37be4c8..9ee2383 100644 --- a/git-cliff-core/src/release.rs +++ b/git-cliff-core/src/release.rs @@ -21,6 +21,8 @@ use serde::{ pub struct Release<'a> { /// Release version, git tag. pub version: Option, + /// git tag's message. + pub message: Option, /// Commits made for the release. pub commits: Vec>, /// Commit ID of the tag. @@ -101,13 +103,28 @@ impl<'a> Release<'a> { } } } - let next_version = VersionUpdater::new() + let mut next_version = VersionUpdater::new() .with_features_always_increment_minor( config.features_always_bump_minor.unwrap_or(true), ) .with_breaking_always_increment_major( config.breaking_always_bump_major.unwrap_or(true), - ) + ); + if let Some(custom_major_increment_regex) = + &config.custom_major_increment_regex + { + next_version = next_version.with_custom_major_increment_regex( + custom_major_increment_regex, + )?; + } + if let Some(custom_minor_increment_regex) = + &config.custom_minor_increment_regex + { + next_version = next_version.with_custom_minor_increment_regex( + custom_minor_increment_regex, + )?; + } + let next_version = next_version .increment( &semver?, self.commits @@ -161,6 +178,7 @@ mod test { fn build_release<'a>(version: &str, commits: &'a [&str]) -> Release<'a> { Release { version: None, + message: None, commits: commits .iter() .map(|v| Commit::from(v.to_string())) @@ -259,9 +277,11 @@ mod test { let release = build_release(version, commits); let next_version = release.calculate_next_version_with_config(&Bump { - features_always_bump_minor: Some(false), - breaking_always_bump_major: Some(false), - initial_tag: None, + features_always_bump_minor: Some(false), + breaking_always_bump_major: Some(false), + initial_tag: None, + custom_major_increment_regex: None, + custom_minor_increment_regex: None, })?; assert_eq!(expected_version, &next_version); } @@ -280,9 +300,11 @@ mod test { let release = build_release(version, commits); let next_version = release.calculate_next_version_with_config(&Bump { - features_always_bump_minor: Some(true), - breaking_always_bump_major: Some(false), - initial_tag: None, + features_always_bump_minor: Some(true), + breaking_always_bump_major: Some(false), + initial_tag: None, + custom_major_increment_regex: None, + custom_minor_increment_regex: None, })?; assert_eq!(expected_version, &next_version); } @@ -301,9 +323,11 @@ mod test { let release = build_release(version, commits); let next_version = release.calculate_next_version_with_config(&Bump { - features_always_bump_minor: Some(false), - breaking_always_bump_major: Some(true), - initial_tag: None, + features_always_bump_minor: Some(false), + breaking_always_bump_major: Some(true), + initial_tag: None, + custom_major_increment_regex: None, + custom_minor_increment_regex: None, })?; assert_eq!(expected_version, &next_version); } @@ -322,9 +346,11 @@ mod test { assert_eq!( "0.1.0", empty_release.calculate_next_version_with_config(&Bump { - features_always_bump_minor: Some(features_always_bump_minor), - breaking_always_bump_major: Some(breaking_always_bump_major), - initial_tag: None, + features_always_bump_minor: Some(features_always_bump_minor), + breaking_always_bump_major: Some(breaking_always_bump_major), + initial_tag: None, + custom_major_increment_regex: None, + custom_minor_increment_regex: None, })? ); } @@ -343,6 +369,7 @@ mod test { let mut release = Release { version: None, + message: None, commits: vec![ Commit::from(String::from( "1d244937ee6ceb8e0314a4a201ba93a7a61f2071 add github \ @@ -629,6 +656,7 @@ mod test { let mut release = Release { version: None, + message: None, commits: vec![ Commit::from(String::from( "1d244937ee6ceb8e0314a4a201ba93a7a61f2071 add github \ @@ -973,6 +1001,7 @@ mod test { let mut release = Release { version: None, + message: None, commits: vec![ Commit::from(String::from( "1d244937ee6ceb8e0314a4a201ba93a7a61f2071 add github \ diff --git a/git-cliff-core/src/repo.rs b/git-cliff-core/src/repo.rs index 0c4ca18..25d9a23 100644 --- a/git-cliff-core/src/repo.rs +++ b/git-cliff-core/src/repo.rs @@ -3,6 +3,7 @@ use crate::error::{ Error, Result, }; +use crate::tag::Tag; use git2::{ BranchType, Commit, @@ -13,11 +14,20 @@ use git2::{ }; use glob::Pattern; use indexmap::IndexMap; -use regex::Regex; +use lazy_regex::{ + lazy_regex, + Lazy, + Regex, +}; use std::io; use std::path::PathBuf; use url::Url; +/// Regex for replacing the signature part of a tag message. +static TAG_SIGNATURE_REGEX: Lazy = lazy_regex!( + r"(?s)-----BEGIN PGP SIGNATURE-----(.*?)-----END PGP SIGNATURE-----" +); + /// Wrapper for [`Repository`] type from git2. /// /// [`Repository`]: GitRepository @@ -98,11 +108,38 @@ impl Repository { /// Returns the current tag. /// /// It is the same as running `git describe --tags` - pub fn current_tag(&self) -> Option { + pub fn current_tag(&self) -> Option { self.inner .describe(DescribeOptions::new().describe_tags()) .ok() - .and_then(|describe| describe.format(None).ok()) + .and_then(|describe| { + describe + .format(None) + .ok() + .map(|name| self.resolve_tag(&name)) + }) + } + + /// Returns the tag object of the given name. + /// + /// If given name doesn't exist, it still returns `Tag` with the given name. + pub fn resolve_tag(&self, name: &str) -> Tag { + match self + .inner + .resolve_reference_from_short_name(name) + .and_then(|r| r.peel_to_tag()) + { + Ok(tag) => Tag { + name: tag.name().unwrap_or_default().to_owned(), + message: tag.message().map(|msg| { + TAG_SIGNATURE_REGEX.replace(msg, "").trim().to_owned() + }), + }, + _ => Tag { + name: name.to_owned(), + message: None, + }, + } } /// Returns the commit object of the given ID. @@ -122,8 +159,8 @@ impl Repository { &self, pattern: &Option, topo_order: bool, - ) -> Result> { - let mut tags: Vec<(Commit, String)> = Vec::new(); + ) -> Result> { + let mut tags: Vec<(Commit, Tag)> = Vec::new(); let tag_names = self.inner.tag_names(None)?; for name in tag_names .iter() @@ -135,14 +172,22 @@ impl Repository { { let obj = self.inner.revparse_single(&name)?; if let Ok(commit) = obj.clone().into_commit() { - tags.push((commit, name)); + tags.push((commit, Tag { + name, + message: None, + })); } else if let Some(tag) = obj.as_tag() { if let Some(commit) = tag .target() .ok() .and_then(|target| target.into_commit().ok()) { - tags.push((commit, name)); + tags.push((commit, Tag { + name: tag.name().map(String::from).unwrap_or(name), + message: tag.message().map(|msg| { + TAG_SIGNATURE_REGEX.replace(msg, "").trim().to_owned() + }), + })); } } } @@ -264,7 +309,7 @@ mod test { fn get_latest_tag() -> Result<()> { let repository = get_repository()?; let tags = repository.tags(&None, false)?; - assert_eq!(&get_last_tag()?, tags.last().expect("no tags found").1); + assert_eq!(get_last_tag()?, tags.last().expect("no tags found").1.name); Ok(()) } @@ -273,16 +318,20 @@ mod test { let repository = get_repository()?; let tags = repository.tags(&None, true)?; assert_eq!( - tags.get("2b8b4d3535f29231e05c3572e919634b9af907b6").expect( - "the commit hash does not exist in the repository (tag v0.1.0)" - ), + tags.get("2b8b4d3535f29231e05c3572e919634b9af907b6") + .expect( + "the commit hash does not exist in the repository (tag v0.1.0)" + ) + .name, "v0.1.0" ); assert_eq!( - tags.get("4ddef08debfff48117586296e49d5caa0800d1b5").expect( - "the commit hash does not exist in the repository (tag \ - v0.1.0-beta.4)" - ), + tags.get("4ddef08debfff48117586296e49d5caa0800d1b5") + .expect( + "the commit hash does not exist in the repository (tag \ + v0.1.0-beta.4)" + ) + .name, "v0.1.0-beta.4" ); let tags = repository.tags( @@ -293,9 +342,11 @@ mod test { true, )?; assert_eq!( - tags.get("2b8b4d3535f29231e05c3572e919634b9af907b6").expect( - "the commit hash does not exist in the repository (tag v0.1.0)" - ), + tags.get("2b8b4d3535f29231e05c3572e919634b9af907b6") + .expect( + "the commit hash does not exist in the repository (tag v0.1.0)" + ) + .name, "v0.1.0" ); assert!(!tags.contains_key("4ddef08debfff48117586296e49d5caa0800d1b5")); @@ -316,4 +367,30 @@ mod test { ); Ok(()) } + + #[test] + fn resolves_existing_tag_with_name_and_message() -> Result<()> { + let repository = get_repository()?; + let tag = repository.resolve_tag("v0.2.3"); + assert_eq!(tag.name, "v0.2.3"); + assert_eq!( + tag.message, + Some( + "Release v0.2.3\n\nBug Fixes\n- Fetch the dependencies before \ + copying the file to embed (9e29c95)" + .to_string() + ) + ); + + Ok(()) + } + + #[test] + fn resolves_tag_when_no_tags_exist() -> Result<()> { + let repository = get_repository()?; + let tag = repository.resolve_tag("nonexistent-tag"); + assert_eq!(tag.name, "nonexistent-tag"); + assert_eq!(tag.message, None); + Ok(()) + } } diff --git a/git-cliff-core/src/tag.rs b/git-cliff-core/src/tag.rs new file mode 100644 index 00000000..7fe8ab9 --- /dev/null +++ b/git-cliff-core/src/tag.rs @@ -0,0 +1,47 @@ +/// Common tag object that is parsed from a repository. +/// +/// Lightweight tags will have `None` as message. +#[derive(Debug)] +pub struct Tag { + /// The name of the tag + pub name: String, + /// The message of the tag (only if it was annotated). + pub message: Option, +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn create_tag_with_name_and_message() { + let tag = Tag { + name: String::from("v1.0"), + message: Some(String::from("Initial release")), + }; + assert_eq!(tag.name, "v1.0"); + assert_eq!(tag.message, Some(String::from("Initial release"))); + } + + #[test] + fn create_tag_with_name_and_no_message() { + let tag = Tag { + name: String::from("v1.0"), + message: None, + }; + assert_eq!(tag.name, "v1.0"); + assert_eq!(tag.message, None); + } + + #[test] + fn debug_print_tag_with_message() { + let tag = Tag { + name: String::from("v1.0"), + message: Some(String::from("Initial release")), + }; + assert_eq!( + format!("{:?}", tag), + "Tag { name: \"v1.0\", message: Some(\"Initial release\") }" + ); + } +} diff --git a/git-cliff-core/src/template.rs b/git-cliff-core/src/template.rs index 11555ae..6d2e611 100644 --- a/git-cliff-core/src/template.rs +++ b/git-cliff-core/src/template.rs @@ -189,6 +189,7 @@ mod test { fn get_fake_release_data() -> Release<'static> { Release { version: Some(String::from("1.0")), + message: None, commits: vec![ Commit::new( String::from("123123"), diff --git a/git-cliff-core/tests/integration_test.rs b/git-cliff-core/tests/integration_test.rs index e120f4f..47f2791 100644 --- a/git-cliff-core/tests/integration_test.rs +++ b/git-cliff-core/tests/integration_test.rs @@ -149,6 +149,7 @@ fn generate_changelog() -> Result<()> { let releases = vec![ Release { version: Some(String::from("v2.0.0")), + message: None, commits: vec![ Commit::new( @@ -213,6 +214,7 @@ fn generate_changelog() -> Result<()> { }, Release { version: Some(String::from("v1.0.0")), + message: None, commits: vec![ Commit::new( String::from("0bc123"), diff --git a/git-cliff/Cargo.toml b/git-cliff/Cargo.toml index 96d86a3..d259219 100644 --- a/git-cliff/Cargo.toml +++ b/git-cliff/Cargo.toml @@ -45,7 +45,7 @@ secrecy.workspace = true lazy_static.workspace = true dirs.workspace = true clap = { version = "4.5.7", features = ["derive", "env", "wrap_help", "cargo"] } -clap_complete = "4.5.5" +clap_complete = "4.5.6" clap_mangen = "0.2.21" shellexpand = "3.1.0" update-informer = { version = "1.1.0", optional = true } diff --git a/git-cliff/src/args.rs b/git-cliff/src/args.rs index 1fcd1c3..4c76985 100644 --- a/git-cliff/src/args.rs +++ b/git-cliff/src/args.rs @@ -64,7 +64,7 @@ pub struct Opt { help = "Prints help information", help_heading = "FLAGS" )] - pub help: Option, + pub help: Option, #[arg( short = 'V', long, @@ -73,10 +73,10 @@ pub struct Opt { help = "Prints version information", help_heading = "FLAGS" )] - pub version: Option, + pub version: Option, /// Increases the logging verbosity. #[arg(short, long, action = ArgAction::Count, alias = "debug", help_heading = Some("FLAGS"))] - pub verbose: u8, + pub verbose: u8, /// Writes the default configuration file to cliff.toml #[arg( short, @@ -85,7 +85,7 @@ pub struct Opt { num_args = 0..=1, required = false )] - pub init: Option>, + pub init: Option>, /// Sets the configuration file. #[arg( short, @@ -95,7 +95,7 @@ pub struct Opt { default_value = DEFAULT_CONFIG, value_parser = Opt::parse_dir )] - pub config: PathBuf, + pub config: PathBuf, /// Sets the working directory. #[arg( short, @@ -104,7 +104,7 @@ pub struct Opt { value_name = "PATH", value_parser = Opt::parse_dir )] - pub workdir: Option, + pub workdir: Option, /// Sets the git repository. #[arg( short, @@ -114,7 +114,7 @@ pub struct Opt { num_args(1..), value_parser = Opt::parse_dir )] - pub repository: Option>, + pub repository: Option>, /// Sets the path to include related commits. #[arg( long, @@ -122,7 +122,7 @@ pub struct Opt { value_name = "PATTERN", num_args(1..) )] - pub include_path: Option>, + pub include_path: Option>, /// Sets the path to exclude related commits. #[arg( long, @@ -130,10 +130,10 @@ pub struct Opt { value_name = "PATTERN", num_args(1..) )] - pub exclude_path: Option>, + pub exclude_path: Option>, /// Sets the regex for matching git tags. #[arg(long, env = "GIT_CLIFF_TAG_PATTERN", value_name = "PATTERN")] - pub tag_pattern: Option, + pub tag_pattern: Option, /// Sets custom commit messages to include in the changelog. #[arg( long, @@ -141,10 +141,18 @@ pub struct Opt { value_name = "MSG", num_args(1..) )] - pub with_commit: Option>, + pub with_commit: Option>, + /// Sets custom message for the latest release. + #[arg( + long, + env = "GIT_CLIFF_WITH_TAG_MESSAGE", + value_name = "MSG", + num_args = 0..=1, + )] + pub with_tag_message: Option, /// Sets the tags to ignore in the changelog. #[arg(long, env = "GIT_CLIFF_IGNORE_TAGS", value_name = "PATTERN")] - pub ignore_tags: Option, + pub ignore_tags: Option, /// Sets commits that will be skipped in the changelog. #[arg( long, @@ -152,7 +160,7 @@ pub struct Opt { value_name = "SHA1", num_args(1..) )] - pub skip_commit: Option>, + pub skip_commit: Option>, /// Prepends entries to the given changelog file. #[arg( short, @@ -161,7 +169,7 @@ pub struct Opt { value_name = "PATH", value_parser = Opt::parse_dir )] - pub prepend: Option, + pub prepend: Option, /// Writes output to the given file. #[arg( short, @@ -172,7 +180,7 @@ pub struct Opt { num_args = 0..=1, default_missing_value = DEFAULT_OUTPUT )] - pub output: Option, + pub output: Option, /// Sets the tag for the latest version. #[arg( short, @@ -181,13 +189,13 @@ pub struct Opt { value_name = "TAG", allow_hyphen_values = true )] - pub tag: Option, + pub tag: Option, /// Bumps the version for unreleased changes. #[arg(long, help_heading = Some("FLAGS"))] - pub bump: bool, + pub bump: bool, /// Prints bumped version for unreleased changes. #[arg(long, help_heading = Some("FLAGS"))] - pub bumped_version: bool, + pub bumped_version: bool, /// Sets the template for the changelog body. #[arg( short, @@ -196,38 +204,38 @@ pub struct Opt { value_name = "TEMPLATE", allow_hyphen_values = true )] - pub body: Option, + pub body: Option, /// Processes the commits starting from the latest tag. #[arg(short, long, help_heading = Some("FLAGS"))] - pub latest: bool, + pub latest: bool, /// Processes the commits that belong to the current tag. #[arg(long, help_heading = Some("FLAGS"))] - pub current: bool, + pub current: bool, /// Processes the commits that do not belong to a tag. #[arg(short, long, help_heading = Some("FLAGS"))] - pub unreleased: bool, + pub unreleased: bool, /// Sorts the tags topologically. #[arg(long, help_heading = Some("FLAGS"))] - pub topo_order: bool, + pub topo_order: bool, /// Disables the external command execution. #[arg(long, help_heading = Some("FLAGS"))] - pub no_exec: bool, + pub no_exec: bool, /// Prints changelog context as JSON. #[arg(short = 'x', long, help_heading = Some("FLAGS"))] - pub context: bool, + pub context: bool, /// Strips the given parts from the changelog. #[arg(short, long, value_name = "PART", value_enum)] - pub strip: Option, + pub strip: Option, /// Sets sorting of the commits inside sections. #[arg( long, value_enum, default_value_t = Sort::Oldest )] - pub sort: Sort, + pub sort: Sort, /// Sets the commit range to process. #[arg(value_name = "RANGE", help_heading = Some("ARGS"))] - pub range: Option, + pub range: Option, /// Sets the GitHub API token. #[arg( long, @@ -236,7 +244,7 @@ pub struct Opt { hide_env_values = true, hide = !cfg!(feature = "github"), )] - pub github_token: Option, + pub github_token: Option, /// Sets the GitHub repository. #[arg( long, @@ -245,7 +253,7 @@ pub struct Opt { value_name = "OWNER/REPO", hide = !cfg!(feature = "github"), )] - pub github_repo: Option, + pub github_repo: Option, /// Sets the GitLab API token. #[arg( long, @@ -254,7 +262,7 @@ pub struct Opt { hide_env_values = true, hide = !cfg!(feature = "gitlab"), )] - pub gitlab_token: Option, + pub gitlab_token: Option, /// Sets the GitLab repository. #[arg( long, @@ -263,7 +271,7 @@ pub struct Opt { value_name = "OWNER/REPO", hide = !cfg!(feature = "gitlab"), )] - pub gitlab_repo: Option, + pub gitlab_repo: Option, /// Sets the Gitea API token. #[arg( long, @@ -272,7 +280,7 @@ pub struct Opt { hide_env_values = true, hide = !cfg!(feature = "gitea"), )] - pub gitea_token: Option, + pub gitea_token: Option, /// Sets the GitLab repository. #[arg( long, @@ -281,7 +289,7 @@ pub struct Opt { value_name = "OWNER/REPO", hide = !cfg!(feature = "gitea"), )] - pub gitea_repo: Option, + pub gitea_repo: Option, /// Sets the Bitbucket API token. #[arg( long, @@ -290,7 +298,7 @@ pub struct Opt { hide_env_values = true, hide = !cfg!(feature = "bitbucket"), )] - pub bitbucket_token: Option, + pub bitbucket_token: Option, /// Sets the Bitbucket repository. #[arg( long, @@ -299,7 +307,7 @@ pub struct Opt { value_name = "OWNER/REPO", hide = !cfg!(feature = "bitbucket"), )] - pub bitbucket_repo: Option, + pub bitbucket_repo: Option, } /// Custom type for the remote value. diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index 6b0125d..a42da54 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -90,7 +90,9 @@ fn process_repository<'a>( let ignore_regex = config.git.ignore_tags.as_ref(); tags = tags .into_iter() - .filter(|(_, name)| { + .filter(|(_, tag)| { + let name = &tag.name; + // Keep skip tags to drop commits in the later stage. let skip = skip_regex.map(|r| r.is_match(name)).unwrap_or_default(); @@ -184,7 +186,7 @@ fn process_repository<'a>( repository.current_tag().as_ref().and_then(|tag| { tags.iter() .enumerate() - .find(|(_, (_, v))| v == &tag) + .find(|(_, (_, v))| v.name == tag.name) .map(|(i, _)| i) }) { match current_tag_index.checked_sub(1) { @@ -226,10 +228,10 @@ fn process_repository<'a>( if let Some(commit_id) = commits.first().map(|c| c.id().to_string()) { match tags.get(&commit_id) { Some(tag) => { - warn!("There is already a tag ({}) for {}", tag, commit_id) + warn!("There is already a tag ({:?}) for {}", tag, commit_id) } None => { - tags.insert(commit_id, tag.to_string()); + tags.insert(commit_id, repository.resolve_tag(tag)); } } } @@ -251,9 +253,13 @@ fn process_repository<'a>( releases[release_index].commits.push(commit); } if let Some(tag) = tags.get(&commit_id) { - releases[release_index].version = Some(tag.to_string()); + let tag_name = &tag.name; + + releases[release_index].version = Some(tag_name.clone()); + releases[release_index].message = tag.message.clone(); releases[release_index].commit_id = Some(commit_id); - releases[release_index].timestamp = if args.tag.as_deref() == Some(tag) { + releases[release_index].timestamp = if args.tag == Some(tag_name.clone()) + { SystemTime::now() .duration_since(UNIX_EPOCH)? .as_secs() @@ -288,6 +294,13 @@ fn process_repository<'a>( } } + // Set custom message for the latest release. + if let Some(message) = &args.with_tag_message { + if let Some(latest_release) = releases.iter_mut().last() { + latest_release.message = Some(message.to_owned()); + } + } + // Set the previous release if the first release does not have one set. if !releases.is_empty() && releases @@ -301,7 +314,7 @@ fn process_repository<'a>( .map(|tag| { tags.iter() .enumerate() - .find(|(_, (_, v))| v == &tag) + .find(|(_, (_, v))| v.name == tag.name) .and_then(|(i, _)| i.checked_sub(1)) .and_then(|i| tags.get_index(i)) }) @@ -309,10 +322,10 @@ fn process_repository<'a>( .flatten(); // Set the previous release if the first tag is found. - if let Some((commit_id, version)) = first_tag { + if let Some((commit_id, tag)) = first_tag { let previous_release = Release { commit_id: Some(commit_id.to_string()), - version: Some(version.to_string()), + version: Some(tag.name.clone()), timestamp: repository .find_commit(commit_id.to_string()) .map(|v| v.time().seconds()) diff --git a/website/docs/configuration/bump.md b/website/docs/configuration/bump.md index ec39a56..5f0f5bc 100644 --- a/website/docs/configuration/bump.md +++ b/website/docs/configuration/bump.md @@ -33,3 +33,51 @@ When `false`, a breaking change commit will trigger: Configures the initial version of the project. When set, the version will be set to this value if no tags are found. + +### custom_major_increment_regex & custom_minor_increment_regex + +Configures additional commit types that should increment the major or minor accordingly. + +They should be used rarely, only in the case you have a spacial case for incrementing versions. + +Expects a valid regex pattern. + +For example: + +```toml +[bump] +features_always_bump_minor = true +breaking_always_bump_major = true +custom_major_increment_regex = "major" +custom_minor_increment_regex = "minor|more" +``` + +with this history: + +``` +5189568 (HEAD -> main) major: 1 +0b17b48 (tag: 0.1.0) initial commit +``` + +will result in: + +```bash +git-cliff --bumped-version + +1.0.0 +``` + +or, with history: + +``` +47206d0 (HEAD -> main) more: 1 +0b17b48 (tag: 0.1.0) initial commit +``` + +will result in: + +```bash +git-cliff --bumped-version + +0.2.0 +``` diff --git a/website/docs/templating/context.md b/website/docs/templating/context.md index 5af9790..c979659 100644 --- a/website/docs/templating/context.md +++ b/website/docs/templating/context.md @@ -25,6 +25,7 @@ following context is generated to use for templating: ```json { "version": "v0.1.0-rc.21", + "message": "The annotated tag message for the release" "commits": [ { "id": "e795460c9bb7275294d1fa53a9d73258fb51eb10", @@ -130,6 +131,7 @@ If [`conventional_commits`](/docs/configuration/git#conventional_commits) is set ```json { "version": "v0.1.0-rc.21", + "message": "The annotated tag message for the release" "commits": [ { "id": "e795460c9bb7275294d1fa53a9d73258fb51eb10", diff --git a/website/docs/usage/adding-tag-messages.md b/website/docs/usage/adding-tag-messages.md new file mode 100644 index 00000000..7e4f569 --- /dev/null +++ b/website/docs/usage/adding-tag-messages.md @@ -0,0 +1,42 @@ +--- +sidebar_position: 9 +--- + +# Adding version (tag) message + +Sometimes, you might want to include a special message or note related to a version of your project. +This can be used to highlight significant milestones, provide additional context, or share information not captured by individual commit messages. + +There are currently 2 ways of doing this, in both ways, the message is available in the context of the template under the name `message`: + +``` +{% if message %} + {{ message }} +{% endif %}\ +``` + +## Using annotated tags + +The recommended way of adding a version message is to add the message to the tag: + +```bash +git tag v1.0.0 -m "first release, yay!" +``` + +So in the release's context, `message` will be "first release, yay!" (even if it is signed). + +## Using `--with-tag-message` + +If for some reason you don't want to have the message in the tag (or don't have a tag yet) but want to include it in the generated changelog, you can use the `--with-tag-message` flag: + +```bash +git cliff --bump --unreleased --with-tag-message "some text" +``` + +In this case, you can only add a message to the latest release. + +:::note + +Please note that if you use `--with-tag-message` on a version it will ignore the original tag's message and use the one from the argument. + +::: diff --git a/website/docs/usage/args.md b/website/docs/usage/args.md index 5219301..efd9b3e 100644 --- a/website/docs/usage/args.md +++ b/website/docs/usage/args.md @@ -20,33 +20,38 @@ git-cliff [FLAGS] [OPTIONS] [--] [RANGE] --current Processes the commits that belong to the current tag -u, --unreleased Processes the commits that do not belong to a tag --topo-order Sorts the tags topologically --x, --context Prints changelog context as JSON --no-exec Disables the external command execution +-x, --context Prints changelog context as JSON ``` ## Options ``` --i, --init [] Writes the default configuration file to cliff.toml --c, --config Sets the configuration file [env: GIT_CLIFF_CONFIG=] [default: cliff.toml] --w, --workdir Sets the working directory [env: GIT_CLIFF_WORKDIR=] --r, --repository ... Sets the git repository [env: GIT_CLIFF_REPOSITORY=] - --include-path ... Sets the path to include related commits [env: GIT_CLIFF_INCLUDE_PATH=] - --exclude-path ... Sets the path to exclude related commits [env: GIT_CLIFF_EXCLUDE_PATH=] - --tag-pattern Sets the regex for matching git tags [env: GIT_CLIFF_TAG_PATTERN=] - --with-commit ... Sets custom commit messages to include in the changelog [env: GIT_CLIFF_WITH_COMMIT=] - --ignore-tags Sets the tags to ignore in the changelog [env: GIT_CLIFF_IGNORE_TAGS=] - --skip-commit ... Sets commits that will be skipped in the changelog [env: GIT_CLIFF_SKIP_COMMIT=] --p, --prepend Prepends entries to the given changelog file [env: GIT_CLIFF_PREPEND=] --o, --output [] Writes output to the given file [env: GIT_CLIFF_OUTPUT=] --t, --tag Sets the tag for the latest version [env: GIT_CLIFF_TAG=] --b, --body