From 254b126094403ad396a0bc17676599d25878c9bb Mon Sep 17 00:00:00 2001 From: Michael Daniels Date: Sat, 13 Jun 2026 13:36:27 -0400 Subject: [PATCH] Add ignore_conventional_merge_commits option Fixes #1927 --- ...mmits_setting_to_changes_config_section.md | 8 +++++ crates/knope-config/src/lib.rs | 6 +++- crates/knope/src/config/mod.rs | 20 +++++------ crates/knope/src/integrations/git.rs | 8 ++++- crates/knope/src/main.rs | 4 +-- crates/knope/src/state.rs | 7 ++-- crates/knope/src/step/command.rs | 4 +-- .../src/step/releases/conventional_commits.rs | 3 +- crates/knope/src/step/releases/mod.rs | 2 +- crates/knope/src/step/releases/package.rs | 6 ++-- crates/knope/src/variables.rs | 6 ++-- crates/knope/tests/helpers/git.rs | 11 +++++- .../prepare_release/branching_history/mod.rs | 1 + .../dryrun_stdout.log | 11 ++++++ .../in/CHANGELOG.md | 13 +++++++ .../in/Cargo.toml | 3 ++ .../in/knope.toml | 12 +++++++ .../mod.rs | 35 +++++++++++++++++++ .../out/CHANGELOG.md | 19 ++++++++++ .../out/Cargo.toml | 3 ++ .../docs/reference/Config File/changes.mdx | 13 +++++++ 21 files changed, 167 insertions(+), 28 deletions(-) create mode 100644 .changeset/add_ignore_conventional_merge_commits_setting_to_changes_config_section.md create mode 100644 crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/dryrun_stdout.log create mode 100644 crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/in/CHANGELOG.md create mode 100644 crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/in/Cargo.toml create mode 100644 crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/in/knope.toml create mode 100644 crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/mod.rs create mode 100644 crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/out/CHANGELOG.md create mode 100644 crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/out/Cargo.toml diff --git a/.changeset/add_ignore_conventional_merge_commits_setting_to_changes_config_section.md b/.changeset/add_ignore_conventional_merge_commits_setting_to_changes_config_section.md new file mode 100644 index 000000000..cd2aef7d7 --- /dev/null +++ b/.changeset/add_ignore_conventional_merge_commits_setting_to_changes_config_section.md @@ -0,0 +1,8 @@ +--- +config: minor +knope: minor +--- + +# Add `ignore_conventional_merge_commits` setting to `[changes]` config section + +You can now ignore merge commits for conventional commit purposes. diff --git a/crates/knope-config/src/lib.rs b/crates/knope-config/src/lib.rs index 913e66bbf..11fb3af0e 100644 --- a/crates/knope-config/src/lib.rs +++ b/crates/knope-config/src/lib.rs @@ -13,11 +13,15 @@ pub use self::{ pub use crate::release_notes::ReleaseNotes; /// Configuration for how changes are tracked and processed -#[derive(Debug, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Changes { /// If set to true, conventional commits are ignored across all workflows #[serde(default, skip_serializing_if = "std::ops::Not::not")] pub ignore_conventional_commits: bool, + /// If set to true, merge commits do not count as conventional commits, + /// even if they would normally be parsed as such. + #[serde(default, skip_serializing_if = "std::ops::Not::not")] + pub ignore_conventional_merge_commits: bool, } #[derive(Debug, Default, Deserialize)] diff --git a/crates/knope/src/config/mod.rs b/crates/knope/src/config/mod.rs index d4cb54313..2b083c905 100644 --- a/crates/knope/src/config/mod.rs +++ b/crates/knope/src/config/mod.rs @@ -21,6 +21,7 @@ use crate::{ mod package; mod toml; +pub(crate) use knope_config::Changes; pub(crate) use toml::{GitHub, Gitea, Jira}; use crate::fs::WriteType; @@ -28,6 +29,7 @@ use crate::fs::WriteType; /// A valid config, loaded from a supported file (or detected via default) #[derive(Debug)] pub(crate) struct Config { + pub(crate) changes: Changes, pub(crate) release_notes: ReleaseNotes, pub(crate) packages: Vec, /// The list of defined workflows that are selectable @@ -38,8 +40,6 @@ pub(crate) struct Config { pub(crate) github: Option, /// Optional configuration to communicate with a Gitea instance pub(crate) gitea: Option, - /// If set to true, conventional commits are ignored across all workflows - pub(crate) ignore_conventional_commits: bool, } impl Config { @@ -77,7 +77,7 @@ impl Config { if let Step::PrepareRelease(prepare_release) = step { if prepare_release.ignore_conventional_commits { // Move to top-level config - self.ignore_conventional_commits = true; + self.changes.ignore_conventional_commits = true; prepare_release.ignore_conventional_commits = false; upgraded = true; } @@ -118,9 +118,12 @@ impl Config { ) }; - let changes = if self.ignore_conventional_commits { + let changes = if self.changes.ignore_conventional_commits + || self.changes.ignore_conventional_merge_commits + { Some(knope_config::Changes { - ignore_conventional_commits: true, + ignore_conventional_commits: self.changes.ignore_conventional_commits, + ignore_conventional_merge_commits: self.changes.ignore_conventional_merge_commits, }) } else { None @@ -199,10 +202,7 @@ impl TryFrom<(ConfigLoader, String)> for Config { jira: config.jira.map(Spanned::into_inner), github: config.github.map(Spanned::into_inner), gitea: config.gitea.map(Spanned::into_inner), - ignore_conventional_commits: config - .shared - .changes - .is_some_and(|c| c.ignore_conventional_commits), + changes: config.shared.changes.unwrap_or_default(), }) } } @@ -332,7 +332,7 @@ pub(crate) fn generate() -> Result { github, gitea, packages, - ignore_conventional_commits: false, + changes: Changes::default(), }) } diff --git a/crates/knope/src/integrations/git.rs b/crates/knope/src/integrations/git.rs index 4bcd0edd1..feed09e1c 100644 --- a/crates/knope/src/integrations/git.rs +++ b/crates/knope/src/integrations/git.rs @@ -364,7 +364,10 @@ pub(crate) fn add_files(file_names: &[RelativePathBuf]) -> Result<(), Error> { /// means that there could be paths which jump _behind_ the target tag... and we want to exclude /// those as well. There's probably a way to optimize performance with some cool graph magic /// eventually, but this is good enough for now. -pub(crate) fn get_commit_messages_after_tag(tag: Option<&str>) -> Result, Error> { +pub(crate) fn get_commit_messages_after_tag( + tag: Option<&str>, + ignore_merge_commits: bool, +) -> Result, Error> { let repo = Repository::open(".")?; let tag_ref = if let Some(tag) = tag { @@ -406,6 +409,9 @@ pub(crate) fn get_commit_messages_after_tag(tag: Option<&str>) -> Result 1 { + continue; + } if let Some(message) = commit.message().map(String::from) { commits.push(Commit { message, diff --git a/crates/knope/src/main.rs b/crates/knope/src/main.rs index 53225f5b8..5b43ae3f2 100644 --- a/crates/knope/src/main.rs +++ b/crates/knope/src/main.rs @@ -216,7 +216,7 @@ fn create_state( jira, github, gitea, - ignore_conventional_commits, + changes, } = config; let git_tags = if packages.is_empty() { // Don't mess with Git if there aren't any packages defined @@ -275,7 +275,7 @@ fn create_state( packages, versioned_files, git_tags, - ignore_conventional_commits, + Some(changes), ); Ok((state, workflows)) } diff --git a/crates/knope/src/state.rs b/crates/knope/src/state.rs index eee0f9a6e..f61ccbcf2 100644 --- a/crates/knope/src/state.rs +++ b/crates/knope/src/state.rs @@ -23,8 +23,7 @@ pub(crate) struct State { pub(crate) all_versioned_files: Vec, pub(crate) pending_actions: Vec, pub(crate) all_git_tags: Vec, - /// If set to true, conventional commits are ignored across all workflows - pub(crate) ignore_conventional_commits: bool, + pub(crate) changes_config: config::Changes, } impl State { @@ -36,7 +35,7 @@ impl State { packages: Vec, all_versioned_files: Vec, all_git_tags: Vec, - ignore_conventional_commits: bool, + changes_config: Option, ) -> Self { State { jira_config, @@ -49,7 +48,7 @@ impl State { all_versioned_files, all_git_tags, pending_actions: Vec::new(), - ignore_conventional_commits, + changes_config: changes_config.unwrap_or_default(), } } } diff --git a/crates/knope/src/step/command.rs b/crates/knope/src/step/command.rs index 66f855ee7..8cb04b59b 100644 --- a/crates/knope/src/step/command.rs +++ b/crates/knope/src/step/command.rs @@ -66,7 +66,7 @@ mod test_run_command { Vec::new(), Vec::new(), Vec::new(), - false, + None, )), command.to_string(), false, @@ -83,7 +83,7 @@ mod test_run_command { Vec::new(), Vec::new(), Vec::new(), - false, + None, )), String::from("exit 1"), false, diff --git a/crates/knope/src/step/releases/conventional_commits.rs b/crates/knope/src/step/releases/conventional_commits.rs index 3ac434e1a..dd9ceabf9 100644 --- a/crates/knope/src/step/releases/conventional_commits.rs +++ b/crates/knope/src/step/releases/conventional_commits.rs @@ -8,6 +8,7 @@ use crate::integrations::git::{self, get_commit_messages_after_tag}; pub(crate) fn get_conventional_commits_after_last_stable_version( package_name: &package::Name, all_tags: &[String], + ignore_merge_commits: bool, ) -> Result, git::Error> { debug!( "Getting conventional commits since last release of package {}", @@ -17,5 +18,5 @@ pub(crate) fn get_conventional_commits_after_last_stable_version( .stable() .map(|target_version| ReleaseTag::new(&target_version.into(), package_name)); - get_commit_messages_after_tag(tag.as_ref().map(ReleaseTag::as_str)) + get_commit_messages_after_tag(tag.as_ref().map(ReleaseTag::as_str), ignore_merge_commits) } diff --git a/crates/knope/src/step/releases/mod.rs b/crates/knope/src/step/releases/mod.rs index a3c8c7c12..4a96c2152 100644 --- a/crates/knope/src/step/releases/mod.rs +++ b/crates/knope/src/step/releases/mod.rs @@ -53,7 +53,7 @@ pub(crate) async fn prepare_release( prepare_release, &state.all_git_tags, &changeset, - state.ignore_conventional_commits, + &state.changes_config, )?; if !changes.is_empty() diff --git a/crates/knope/src/step/releases/package.rs b/crates/knope/src/step/releases/package.rs index 08134da40..ce07ac851 100644 --- a/crates/knope/src/step/releases/package.rs +++ b/crates/knope/src/step/releases/package.rs @@ -100,7 +100,7 @@ impl Package { prepare_release: &PrepareRelease, all_tags: &[String], changeset: &[changesets::Release], - global_ignore_conventional_commits: bool, + changes_config: &config::Changes, ) -> Result, Error> { let ignore_conventional_commits = prepare_release.ignore_conventional_commits; @@ -114,7 +114,8 @@ impl Package { } // Use step-level setting if present, otherwise use global setting - let should_ignore = ignore_conventional_commits || global_ignore_conventional_commits; + let should_ignore = + ignore_conventional_commits || changes_config.ignore_conventional_commits; let commit_messages = if should_ignore { Vec::new() @@ -122,6 +123,7 @@ impl Package { conventional_commits::get_conventional_commits_after_last_stable_version( &self.versioning.name, all_tags, + changes_config.ignore_conventional_merge_commits, )? }; diff --git a/crates/knope/src/variables.rs b/crates/knope/src/variables.rs index bf1476019..ca53ab7fc 100644 --- a/crates/knope/src/variables.rs +++ b/crates/knope/src/variables.rs @@ -127,7 +127,7 @@ mod test_replace_variables { use relative_path::RelativePathBuf; use super::*; - use crate::step::issues::Issue; + use crate::{config, step::issues::Issue}; fn state() -> State { let changelog = Changelog::new(RelativePathBuf::default(), String::new()); @@ -166,7 +166,7 @@ mod test_replace_variables { vec![package], all_versioned_files, Vec::new(), - false, + None, ) } @@ -209,7 +209,7 @@ mod test_replace_variables { all_git_tags: Vec::new(), all_versioned_files: Vec::new(), pending_actions: Vec::new(), - ignore_conventional_commits: false, + changes_config: config::Changes::default(), }; let result = diff --git a/crates/knope/tests/helpers/git.rs b/crates/knope/tests/helpers/git.rs index 794845d4d..dcb8b76e7 100644 --- a/crates/knope/tests/helpers/git.rs +++ b/crates/knope/tests/helpers/git.rs @@ -142,13 +142,22 @@ pub fn switch_branch(path: &Path, name: &str) { debug!("{}", String::from_utf8_lossy(&output.stdout)); } -/// Merge a branch into the current branch +/// Merge a branch into the current branch, with the default message pub fn merge_branch(path: &Path, name: &str) { + merge_branch_with_message(path, name, None); +} + +/// Merge a branch into the current branch with the given message, or the default if None +pub fn merge_branch_with_message(path: &Path, name: &str, message: Option<&str>) { let output = Command::new("git") .arg("merge") .arg("--no-ff") .arg("--no-edit") .arg(name) + .args(match message { + None => vec![], + Some(m) => vec!["-m", m], + }) .current_dir(path) .output() .unwrap(); diff --git a/crates/knope/tests/prepare_release/branching_history/mod.rs b/crates/knope/tests/prepare_release/branching_history/mod.rs index 9ac75c0e1..70dcdffca 100644 --- a/crates/knope/tests/prepare_release/branching_history/mod.rs +++ b/crates/knope/tests/prepare_release/branching_history/mod.rs @@ -1,3 +1,4 @@ mod merge_commits; mod pick_correct_commits; +mod pick_correct_commits_merges_ignored; mod pick_correct_tag; diff --git a/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/dryrun_stdout.log b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/dryrun_stdout.log new file mode 100644 index 000000000..3ccb7d595 --- /dev/null +++ b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/dryrun_stdout.log @@ -0,0 +1,11 @@ +Would add the following to Cargo.toml: version = 2.0.1 +Would add the following to CHANGELOG.md: +## 2.0.1 ([DATE]) + +### Fixes + +- Another bug + +Would add files to git: + Cargo.toml + CHANGELOG.md diff --git a/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/in/CHANGELOG.md b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/in/CHANGELOG.md new file mode 100644 index 000000000..9d405de82 --- /dev/null +++ b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/in/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +## 2.0.0 + +### Breaking Changes + +#### A breaking feature + +## 1.0.1 + +### Bug Fixes + +#### A bug diff --git a/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/in/Cargo.toml b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/in/Cargo.toml new file mode 100644 index 000000000..da9984673 --- /dev/null +++ b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/in/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "default" +version = "2.0.0" diff --git a/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/in/knope.toml b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/in/knope.toml new file mode 100644 index 000000000..b2810e9f0 --- /dev/null +++ b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/in/knope.toml @@ -0,0 +1,12 @@ +[changes] +ignore_conventional_merge_commits = true + +[package] +versioned_files = ["Cargo.toml"] +changelog = "CHANGELOG.md" + +[[workflows]] +name = "prepare-release" + +[[workflows.steps]] +type = "PrepareRelease" diff --git a/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/mod.rs b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/mod.rs new file mode 100644 index 000000000..5503c6d55 --- /dev/null +++ b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/mod.rs @@ -0,0 +1,35 @@ +/// Specifically designed to catch +use crate::helpers::{ + TestCase, commit, create_branch, merge_branch, merge_branch_with_message, switch_branch, tag, +}; + +#[test] +fn pick_correct_commits_from_branching_history_merges_ignored() { + let test = TestCase::new(file!()); + let temp_dir = test.arrange(); + let temp_path = temp_dir.path(); + + commit(temp_path, "Initial commit", "Knope "); + tag(temp_path, "v1.0.0"); + create_branch(temp_path, "patch"); + commit(temp_path, "fix: A bug", "Knope "); + switch_branch(temp_path, "main"); + merge_branch(temp_path, "patch"); + tag(temp_path, "v1.0.1"); + create_branch(temp_path, "breaking"); + commit( + temp_path, + "feat!: A breaking feature", + "Knope ", + ); + switch_branch(temp_path, "main"); + merge_branch(temp_path, "breaking"); + tag(temp_path, "v2.0.0"); + switch_branch(temp_path, "breaking"); + merge_branch(temp_path, "main"); + commit(temp_path, "fix: Another bug", "Knope "); + switch_branch(temp_path, "main"); + merge_branch_with_message(temp_path, "breaking", Some("fix: Another bug")); + + test.assert(test.act(temp_dir, "prepare-release")); +} diff --git a/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/out/CHANGELOG.md b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/out/CHANGELOG.md new file mode 100644 index 000000000..94ec8bf05 --- /dev/null +++ b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/out/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog + +## 2.0.1 ([DATE]) + +### Fixes + +- Another bug + +## 2.0.0 + +### Breaking Changes + +#### A breaking feature + +## 1.0.1 + +### Bug Fixes + +#### A bug diff --git a/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/out/Cargo.toml b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/out/Cargo.toml new file mode 100644 index 000000000..5a69984fc --- /dev/null +++ b/crates/knope/tests/prepare_release/branching_history/pick_correct_commits_merges_ignored/out/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "default" +version = "2.0.1" diff --git a/docs/src/content/docs/reference/Config File/changes.mdx b/docs/src/content/docs/reference/Config File/changes.mdx index e5ac2e41e..c6fef53b3 100644 --- a/docs/src/content/docs/reference/Config File/changes.mdx +++ b/docs/src/content/docs/reference/Config File/changes.mdx @@ -24,6 +24,19 @@ This replaces the deprecated `ignore_conventional_commits` option on the [`Prepa To migrate from the old step-level setting to this top-level setting, run `knope --upgrade`. ::: +## `ignore_conventional_merge_commits` + +When set to `true`, Knope will not consider merge commits when determining version bumps and changelog entries. + +```toml title="knope.toml" +[changes] +ignore_conventional_merge_commits = true + +[package] +versioned_files = ["Cargo.toml"] +changelog = "CHANGELOG.md" +``` + [Conventional Commits]: /reference/concepts/conventional-commits [Changesets]: /reference/concepts/changeset [`PrepareRelease` step]: /reference/config-file/steps/prepare-release