Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(changelog): allow adding custom context #613

Merged
merged 7 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
17 changes: 17 additions & 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 @@ -79,6 +79,7 @@ features = ["debug-embed", "compression"]

[dev-dependencies]
pretty_assertions = "1.4.0"
expect-test = "1.5.0"
orhun marked this conversation as resolved.
Show resolved Hide resolved

[package.metadata.docs.rs]
all-features = true
Expand Down
117 changes: 111 additions & 6 deletions git-cliff-core/src/changelog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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>,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(())
}
}
4 changes: 2 additions & 2 deletions git-cliff-core/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>(
orhun marked this conversation as resolved.
Show resolved Hide resolved
&self,
context: &C,
additional_context: Option<&HashMap<S, T>>,
Expand All @@ -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) {
Expand Down
Loading