Skip to content

Commit

Permalink
feat(config): support detecting config from project manifest (#571)
Browse files Browse the repository at this point in the history
* feat(config): support detecting config from project manifest

* refactor: apply clippy suggestions

* test(fixture): add fixture for configuring from Cargo.toml
  • Loading branch information
orhun authored Mar 23, 2024
1 parent f26fbe0 commit 9eb3d65
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 28 deletions.
39 changes: 39 additions & 0 deletions .github/fixtures/test-configure-from-cargo-toml/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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" },
]
11 changes: 11 additions & 0 deletions .github/fixtures/test-configure-from-cargo-toml/commit.sh
Original file line number Diff line number Diff line change
@@ -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"
31 changes: 31 additions & 0 deletions .github/fixtures/test-configure-from-cargo-toml/expected.md
Original file line number Diff line number Diff line change
@@ -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 -->
1 change: 1 addition & 0 deletions .github/workflows/test-fixtures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions git-cliff-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
96 changes: 68 additions & 28 deletions git-cliff-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<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"),
},
];

/// Regex for matching the metadata in pyproject.toml
const PYPROJECT_METADATA_REGEX: &str = r"^\[(?:tool)\.git\-cliff\.";
}

/// Configuration values.
#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -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("__"),
)
Expand All @@ -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"))
Expand Down
2 changes: 2 additions & 0 deletions git-cliff/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down

0 comments on commit 9eb3d65

Please sign in to comment.