From 9eb3d65945d70d04a99a2ea7a3042c404ea6f78b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Orhun=20Parmaks=C4=B1z?= Date: Sat, 23 Mar 2024 17:31:58 +0300 Subject: [PATCH] feat(config): support detecting config from project manifest (#571) * feat(config): support detecting config from project manifest * refactor: apply clippy suggestions * test(fixture): add fixture for configuring from Cargo.toml --- .../test-configure-from-cargo-toml/Cargo.toml | 39 ++++++++ .../test-configure-from-cargo-toml/commit.sh | 11 +++ .../expected.md | 31 ++++++ .github/workflows/test-fixtures.yml | 1 + Cargo.lock | 1 + git-cliff-core/Cargo.toml | 1 + git-cliff-core/src/config.rs | 96 +++++++++++++------ git-cliff/src/lib.rs | 2 + 8 files changed, 154 insertions(+), 28 deletions(-) create mode 100644 .github/fixtures/test-configure-from-cargo-toml/Cargo.toml create mode 100755 .github/fixtures/test-configure-from-cargo-toml/commit.sh create mode 100644 .github/fixtures/test-configure-from-cargo-toml/expected.md diff --git a/.github/fixtures/test-configure-from-cargo-toml/Cargo.toml b/.github/fixtures/test-configure-from-cargo-toml/Cargo.toml new file mode 100644 index 0000000000..d44e73bd9d --- /dev/null +++ b/.github/fixtures/test-configure-from-cargo-toml/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "test" +version = "0.1.0" +edition = "2021" + +[package.metadata.git-cliff.changelog] +# changelog header +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") }} +{% 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 + +[package.metadata.git-cliff.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-configure-from-cargo-toml/commit.sh b/.github/fixtures/test-configure-from-cargo-toml/commit.sh new file mode 100755 index 0000000000..7c6fe32718 --- /dev/null +++ b/.github/fixtures/test-configure-from-cargo-toml/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-configure-from-cargo-toml/expected.md b/.github/fixtures/test-configure-from-cargo-toml/expected.md new file mode 100644 index 0000000000..72e96a6f09 --- /dev/null +++ b/.github/fixtures/test-configure-from-cargo-toml/expected.md @@ -0,0 +1,31 @@ +# 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 + +### Bug Fixes + +- Fix feature 1 + +### Features + +- Add feature 1 + + diff --git a/.github/workflows/test-fixtures.yml b/.github/workflows/test-fixtures.yml index 22b3473de1..2e35cf7010 100644 --- a/.github/workflows/test-fixtures.yml +++ b/.github/workflows/test-fixtures.yml @@ -67,6 +67,7 @@ jobs: command: --no-exec - fixtures-name: test-custom-tag-pattern command: --tag-pattern "alpha.*" + - fixtures-name: test-configure-from-cargo-toml steps: - name: Checkout uses: actions/checkout@v4 diff --git a/Cargo.lock b/Cargo.lock index 19c8457132..ddcb094bd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -748,6 +748,7 @@ dependencies = [ "http-cache-reqwest", "indexmap", "lazy-regex", + "lazy_static", "log", "next_version", "pretty_assertions", diff --git a/git-cliff-core/Cargo.toml b/git-cliff-core/Cargo.toml index 6068e69815..5f9fbb6902 100644 --- a/git-cliff-core/Cargo.toml +++ b/git-cliff-core/Cargo.toml @@ -34,6 +34,7 @@ regex.workspace = true log.workspace = true secrecy.workspace = true dirs.workspace = true +lazy_static.workspace = true thiserror = "1.0.58" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.114" diff --git a/git-cliff-core/src/config.rs b/git-cliff-core/src/config.rs index 90caea4ff0..ad778637d2 100644 --- a/git-cliff-core/src/config.rs +++ b/git-cliff-core/src/config.rs @@ -9,17 +9,42 @@ use serde::{ Deserialize, Serialize, }; -use std::ffi::OsStr; use std::fmt; use std::fs; use std::path::Path; +use std::path::PathBuf; -/// Regex for matching the metadata in Cargo.toml -const CARGO_METADATA_REGEX: &str = - r"^\[(?:workspace|package)\.metadata\.git\-cliff\."; +/// Manifest file information and regex for matching contents. +#[derive(Debug)] +struct ManifestInfo { + /// Path of the manifest. + path: PathBuf, + /// Regular expression for matching metadata in the manifest. + regex: Regex, +} + +lazy_static::lazy_static! { + /// Array containing manifest information for Rust and Python projects. + static ref MANIFEST_INFO: Vec = vec![ + ManifestInfo { + path: PathBuf::from("Cargo.toml"), + regex: RegexBuilder::new( + r"^\[(?:workspace|package)\.metadata\.git\-cliff\.", + ) + .multi_line(true) + .build() + .expect("failed to build regex"), + }, + ManifestInfo { + path: PathBuf::from("pyproject.toml"), + regex: RegexBuilder::new(r"^\[(?:tool)\.git\-cliff\.") + .multi_line(true) + .build() + .expect("failed to build regex"), + }, + ]; -/// Regex for matching the metadata in pyproject.toml -const PYPROJECT_METADATA_REGEX: &str = r"^\[(?:tool)\.git\-cliff\."; +} /// Configuration values. #[derive(Debug, Clone, Serialize, Deserialize)] @@ -232,30 +257,46 @@ pub struct LinkParser { } impl Config { + /// Reads the config file contents from project manifest (e.g. Cargo.toml, + /// pyproject.toml) + pub fn read_from_manifest() -> Result> { + for info in (*MANIFEST_INFO).iter() { + if info.path.exists() { + let contents = fs::read_to_string(&info.path)?; + if info.regex.is_match(&contents) { + return Ok(Some( + info.regex.replace_all(&contents, "[").to_string(), + )); + } + } + } + Ok(None) + } + + /// Parses the config file from string and returns the values. + pub fn parse_from_str(contents: &str) -> Result { + Ok(config::Config::builder() + .add_source(config::File::from_str(contents, config::FileFormat::Toml)) + .add_source( + config::Environment::with_prefix("GIT_CLIFF").separator("__"), + ) + .build()? + .try_deserialize()?) + } + /// Parses the config file and returns the values. pub fn parse(path: &Path) -> Result { - let config_builder = if path.file_name() == Some(OsStr::new("Cargo.toml")) || - path.file_name() == Some(OsStr::new("pyproject.toml")) + if MANIFEST_INFO + .iter() + .any(|v| path.file_name() == v.path.file_name()) { - let contents = fs::read_to_string(path)?; - let metadata_regex = RegexBuilder::new( - if path.file_name() == Some(OsStr::new("Cargo.toml")) { - CARGO_METADATA_REGEX - } else { - PYPROJECT_METADATA_REGEX - }, - ) - .multi_line(true) - .build()?; - let contents = metadata_regex.replace_all(&contents, "["); - config::Config::builder().add_source(config::File::from_str( - &contents, - config::FileFormat::Toml, - )) - } else { - config::Config::builder().add_source(config::File::from(path)) - }; - Ok(config_builder + if let Some(contents) = Self::read_from_manifest()? { + return Self::parse_from_str(&contents); + } + } + + Ok(config::Config::builder() + .add_source(config::File::from(path)) .add_source( config::Environment::with_prefix("GIT_CLIFF").separator("__"), ) @@ -269,7 +310,6 @@ mod test { use super::*; use pretty_assertions::assert_eq; use std::env; - use std::path::PathBuf; #[test] fn parse_config() -> Result<()> { let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index bc7da50639..51c63a7564 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -347,6 +347,8 @@ pub fn run(mut args: Opt) -> Result<()> { config } else if path.exists() { Config::parse(&path)? + } else if let Some(contents) = Config::read_from_manifest()? { + Config::parse_from_str(&contents)? } else { if !args.context { warn!(