1
1
mirror of https://github.com/orhun/git-cliff.git synced 2024-07-07 03:16:19 +03:00

feat(changelog): allow adding custom context (#613)

* feat: allow adding custom context

* chore: add test

* fix: lint

* fix: lint

* fix: lint

* docs: contributing

* chore: polish implementation

---------

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
This commit is contained in:
Marco Ieni 2024-04-21 21:02:41 +02:00 committed by GitHub
parent ade9eae7c0
commit 522bd536a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 138 additions and 9 deletions

View File

@ -31,7 +31,13 @@ cargo build
cargo test
```
6. Make sure [rustfmt](https://github.com/rust-lang/rustfmt) and [clippy](https://github.com/rust-lang/rust-clippy) don't complain about your changes.
6. If needed, update the snapshot tests (i.e. tests using `expect_test`):
```sh
env UPDATE_EXPECT=1 cargo test
```
7. Make sure [rustfmt](https://github.com/rust-lang/rustfmt) and [clippy](https://github.com/rust-lang/rust-clippy) don't complain about your changes.
We use the `nightly` channel for `rustfmt` so please set the appropriate settings for your editor/IDE for that.

17
Cargo.lock generated
View File

@ -521,6 +521,12 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "dissimilar"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632"
[[package]]
name = "doc-comment"
version = "0.3.3"
@ -586,6 +592,16 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "expect-test"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0"
dependencies = [
"dissimilar",
"once_cell",
]
[[package]]
name = "fastrand"
version = "2.0.2"
@ -761,6 +777,7 @@ dependencies = [
"config",
"dirs",
"document-features",
"expect-test",
"futures",
"git-conventional",
"git2",

View File

@ -80,6 +80,7 @@ features = ["debug-embed", "compression"]
[dev-dependencies]
pretty_assertions = "1.4.0"
expect-test = "1.5.0"
[package.metadata.docs.rs]
all-features = true

View File

@ -28,10 +28,11 @@ use std::time::{
#[derive(Debug)]
pub struct Changelog<'a> {
/// Releases that the changelog will contain.
pub releases: Vec<Release<'a>>,
body_template: Template,
footer_template: Option<Template>,
config: &'a Config,
pub releases: Vec<Release<'a>>,
body_template: Template,
footer_template: Option<Template>,
config: &'a Config,
additional_context: HashMap<String, serde_json::Value>,
}
impl<'a> Changelog<'a> {
@ -54,12 +55,30 @@ impl<'a> Changelog<'a> {
None => None,
},
config,
additional_context: HashMap::new(),
};
changelog.process_commits();
changelog.process_releases();
Ok(changelog)
}
/// Adds a key value pair to the template context.
///
/// These values will be used when generating the changelog.
///
/// # Errors
///
/// This operation fails if the deserialization fails.
pub fn add_context(
&mut self,
key: impl Into<String>,
value: impl serde::Serialize,
) -> Result<()> {
self.additional_context
.insert(key.into(), serde_json::to_value(value)?);
Ok(())
}
/// Processes a single commit and returns/logs the result.
fn process_commit(
commit: Commit<'a>,
@ -232,8 +251,11 @@ impl<'a> Changelog<'a> {
/// Generates the changelog and writes it to the given output.
pub fn generate<W: Write>(&self, out: &mut W) -> Result<()> {
debug!("Generating changelog...");
let mut additional_context = HashMap::new();
additional_context.insert("remote", self.config.remote.clone());
let mut additional_context = self.additional_context.clone();
additional_context.insert(
"remote".to_string(),
serde_json::to_value(self.config.remote.clone())?,
);
#[cfg(feature = "github")]
let (github_commits, github_pull_requests) = self.get_github_metadata()?;
let postprocessors = self
@ -818,4 +840,87 @@ chore(deps): fix broken deps
);
Ok(())
}
#[test]
fn changelog_adds_additional_context() -> Result<()> {
let (mut config, releases) = get_test_data();
// add `{{ custom_field }}` to the template
config.changelog.body = Some(
r#"{% if version %}
## {{ custom_field }} [{{ version }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% if commit_id %}({{ commit_id }}){% endif %}{% else %}
## Unreleased{% endif %}
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group }}{% for group, commits in commits | group_by(attribute="scope") %}
#### {{ group }}{% for commit in commits %}
- {{ commit.message }}{% endfor %}
{% endfor %}{% endfor %}"#
.to_string(),
);
let mut changelog = Changelog::new(releases, &config)?;
changelog.add_context("custom_field", "Hello")?;
let mut out = Vec::new();
changelog.generate(&mut out)?;
expect_test::expect![[r#"
# Changelog
## Unreleased
### Bug Fixes
#### app
- fix abc
### New features
#### app
- add xyz
### Other
#### app
- document zyx
#### ui
- do exciting stuff
## Hello [v1.0.0] - 1971-08-02
(0bc123)
### Bug Fixes
#### ui
- fix more stuff
### Documentation
#### documentation
- update docs
- add some documentation
### I love tea
#### app
- damn right
### Matched (group)
#### group
- support regex-replace for groups
### New features
#### app
- add cool features
#### other
- support unscoped commits
- support breaking commits
### Other
#### app
- do nothing
#### other
- support unconventional commits
- this commit is preprocessed
#### ui
- make good stuff
-- total releases: 2 --
"#]]
.assert_eq(str::from_utf8(&out).unwrap_or_default());
Ok(())
}
}

View File

@ -143,7 +143,7 @@ impl Template {
}
/// Renders the template.
pub fn render<C: Serialize, T: Serialize, S: Into<String> + Copy>(
pub fn render<C: Serialize, T: Serialize, S: Into<String> + Clone>(
&self,
context: &C,
additional_context: Option<&HashMap<S, T>>,
@ -152,7 +152,7 @@ impl Template {
let mut context = TeraContext::from_serialize(context)?;
if let Some(additional_context) = additional_context {
for (key, value) in additional_context {
context.insert(*key, &value);
context.insert(key.clone(), &value);
}
}
match self.tera.render("template", &context) {