1
1
mirror of https://github.com/orhun/git-cliff.git synced 2024-08-15 18:00:52 +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 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. 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", "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]] [[package]]
name = "doc-comment" name = "doc-comment"
version = "0.3.3" version = "0.3.3"
@ -586,6 +592,16 @@ dependencies = [
"windows-sys 0.52.0", "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]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.0.2" version = "2.0.2"
@ -761,6 +777,7 @@ dependencies = [
"config", "config",
"dirs", "dirs",
"document-features", "document-features",
"expect-test",
"futures", "futures",
"git-conventional", "git-conventional",
"git2", "git2",

View File

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

View File

@ -32,6 +32,7 @@ pub struct Changelog<'a> {
body_template: Template, body_template: Template,
footer_template: Option<Template>, footer_template: Option<Template>,
config: &'a Config, config: &'a Config,
additional_context: HashMap<String, serde_json::Value>,
} }
impl<'a> Changelog<'a> { impl<'a> Changelog<'a> {
@ -54,12 +55,30 @@ impl<'a> Changelog<'a> {
None => None, None => None,
}, },
config, config,
additional_context: HashMap::new(),
}; };
changelog.process_commits(); changelog.process_commits();
changelog.process_releases(); changelog.process_releases();
Ok(changelog) 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. /// Processes a single commit and returns/logs the result.
fn process_commit( fn process_commit(
commit: Commit<'a>, commit: Commit<'a>,
@ -232,8 +251,11 @@ impl<'a> Changelog<'a> {
/// Generates the changelog and writes it to the given output. /// Generates the changelog and writes it to the given output.
pub fn generate<W: Write>(&self, out: &mut W) -> Result<()> { pub fn generate<W: Write>(&self, out: &mut W) -> Result<()> {
debug!("Generating changelog..."); debug!("Generating changelog...");
let mut additional_context = HashMap::new(); let mut additional_context = self.additional_context.clone();
additional_context.insert("remote", self.config.remote.clone()); additional_context.insert(
"remote".to_string(),
serde_json::to_value(self.config.remote.clone())?,
);
#[cfg(feature = "github")] #[cfg(feature = "github")]
let (github_commits, github_pull_requests) = self.get_github_metadata()?; let (github_commits, github_pull_requests) = self.get_github_metadata()?;
let postprocessors = self let postprocessors = self
@ -818,4 +840,87 @@ chore(deps): fix broken deps
); );
Ok(()) 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. /// 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, &self,
context: &C, context: &C,
additional_context: Option<&HashMap<S, T>>, additional_context: Option<&HashMap<S, T>>,
@ -152,7 +152,7 @@ impl Template {
let mut context = TeraContext::from_serialize(context)?; let mut context = TeraContext::from_serialize(context)?;
if let Some(additional_context) = additional_context { if let Some(additional_context) = additional_context {
for (key, value) in additional_context { for (key, value) in additional_context {
context.insert(*key, &value); context.insert(key.clone(), &value);
} }
} }
match self.tera.render("template", &context) { match self.tera.render("template", &context) {