1
1
mirror of https://github.com/orhun/git-cliff.git synced 2024-09-11 06:55:38 +03:00

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
This commit is contained in:
Orhun Parmaksız 2024-03-23 17:31:58 +03:00 committed by GitHub
parent f26fbe09ff
commit 9eb3d65945
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 154 additions and 28 deletions

View File

@ -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 = """
<!-- generated by git-cliff -->
"""
# 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" },
]

View File

@ -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"

View File

@ -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
<!-- generated by git-cliff -->

View File

@ -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

1
Cargo.lock generated
View File

@ -748,6 +748,7 @@ dependencies = [
"http-cache-reqwest",
"indexmap",
"lazy-regex",
"lazy_static",
"log",
"next_version",
"pretty_assertions",

View File

@ -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"

View File

@ -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,
}
/// Regex for matching the metadata in pyproject.toml
const PYPROJECT_METADATA_REGEX: &str = r"^\[(?:tool)\.git\-cliff\.";
lazy_static::lazy_static! {
/// Array containing manifest information for Rust and Python projects.
static ref MANIFEST_INFO: Vec<ManifestInfo> = 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"),
},
];
}
/// 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<Option<String>> {
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<Config> {
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<Config> {
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"))

View File

@ -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!(