Repository: cocogitto/cocogitto
Pull request #443 (2025-01-14 08:06:43)

feat: add bump order config for packages
**This PR adds the ability to specify the order in which monorepo packages should be bumped.**
This feature is particularly useful for small monorepositories with a limited number of interdependent packages.

**Future Work:**
The next step would involve implementing a plugin system to enable automatic package ordering based on the language or package manager being used.

cocogitto-bot[bot]: :heavy_check_mark: 8d63293a7926fc0da9956f62c9581e6b289245ad - Conventional commits check succeeded.
cocogitto-bot[bot]: :heavy_check_mark: dda67e03f593f758e6c884abe7b2c29e4c7da01e - Conventional commits check succeeded.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/cocogitto/cocogitto/pull/443?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cocogitto) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 86.56%. Comparing base [(`4002591`)](https://app.codecov.io/gh/cocogitto/cocogitto/commit/400259139eec1f34b37191f2d2011ee984c08454?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cocogitto) to head [(`dda67e0`)](https://app.codecov.io/gh/cocogitto/cocogitto/commit/dda67e03f593f758e6c884abe7b2c29e4c7da01e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cocogitto).
> Report is 22 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #443      +/-   ##
==========================================
- Coverage   86.68%   86.56%   -0.13%     
==========================================
  Files          49       49              
  Lines        7046     7116      +70     
==========================================
+ Hits         6108     6160      +52     
- Misses        938      956      +18     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/cocogitto/cocogitto/pull/443?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cocogitto).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cocogitto).

cocogitto-bot[bot]: :heavy_check_mark: dda67e03f593f758e6c884abe7b2c29e4c7da01e...2a039e982af78b94a46f0b2b58a512ea3238adad - Conventional commits check succeeded.

Patch:
diff --git a/.gitignore b/.gitignore
index f77e506..d6486f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ massif.out.*
 perf.data
 perf.data.old
 stacks.folded
+.zed
diff --git a/src/command/bump/monorepo.rs b/src/command/bump/monorepo.rs
index 65828c8..f6982f7 100644
--- a/src/command/bump/monorepo.rs
+++ b/src/command/bump/monorepo.rs
@@ -11,6 +11,7 @@ use crate::conventional::version::{Increment, IncrementCommand};
 
 use crate::git::tag::{Tag, TagLookUpOptions};
 use crate::hook::HookVersion;
+use crate::settings::MonoRepoPackage;
 use crate::{settings, CocoGitto, SETTINGS};
 use anyhow::Result;
 use colored::*;
@@ -449,7 +450,10 @@ impl CocoGitto {
         build: Option<&str>,
     ) -> Result<Vec<PackageBumpData>> {
         let mut package_bumps = vec![];
-        for (package_name, package) in SETTINGS.packages.iter() {
+        let mut packages: Vec<(&String, &MonoRepoPackage)> = SETTINGS.packages.iter().collect();
+        packages.sort_by(|a, b| a.1.bump_order.cmp(&b.1.bump_order));
+
+        for (package_name, package) in packages {
             let old = self.repository.get_latest_package_tag(package_name);
             let old = tag_or_fallback_to_zero(old)?;
 
diff --git a/src/settings/mod.rs b/src/settings/mod.rs
index 900c4da..4e472a0 100644
--- a/src/settings/mod.rs
+++ b/src/settings/mod.rs
@@ -204,7 +204,7 @@ pub enum GitHook {
 #[serde(deny_unknown_fields, default)]
 pub struct MonoRepoPackage {
     /// The package path, relative to the repository root dir.
-    /// Used to scan commits and set hook commands current directory
+    /// Used to scan commits and set hook commands current directory.
     pub path: PathBuf,
     /// List of globs for additional paths to include, relative to
     /// the repository root dir.
@@ -212,17 +212,20 @@ pub struct MonoRepoPackage {
     /// List of globs for paths to ignore, relative to
     /// the repository root dir.
     pub ignore: Vec<String>,
-    /// Where to write the changelog
+    /// Where to write the changelog.
     pub changelog_path: Option<String>,
     /// Bumping package marked as public api will increment
-    /// the global monorepo version when using `cog bump --auto`
+    /// the global monorepo version when using `cog bump --auto`.
     pub public_api: bool,
-    /// Overrides `pre_package_bump_hooks`
+    /// Overrides `pre_package_bump_hooks`.
     pub pre_bump_hooks: Option<Vec<String>>,
-    /// Overrides `post_package_bump_hooks`
+    /// Overrides `post_package_bump_hooks`.
     pub post_bump_hooks: Option<Vec<String>>,
-    /// Custom profile to override `pre_bump_hooks`, `post_bump_hooks`
+    /// Custom profile to override `pre_bump_hooks`, `post_bump_hooks`.
     pub bump_profiles: HashMap<String, BumpProfile>,
+    /// Ordering of packages in the changelog, this affect in which order
+    /// packages will be bumped.
+    pub bump_order: Option<usize>,
 }
 
 impl Default for &MonoRepoPackage {
@@ -236,6 +239,7 @@ impl Default for &MonoRepoPackage {
             post_bump_hooks: None,
             bump_profiles: Default::default(),
             public_api: true,
+            bump_order: None,
         });
 
         Box::leak(package)
@@ -253,6 +257,7 @@ impl Default for MonoRepoPackage {
             post_bump_hooks: None,
             bump_profiles: Default::default(),
             public_api: true,
+            bump_order: None,
         }
     }
 }
diff --git a/website/guide/monorepo.md b/website/guide/monorepo.md
index ec36e60..f759221 100644
--- a/website/guide/monorepo.md
+++ b/website/guide/monorepo.md
@@ -64,6 +64,23 @@ If some of your packages does not belong to your project public API use `public_
 from updating the global project version.
 :::
 
+### Package bump order
+
+When creating tags for multiple packages in a monorepo, you can control the order in which packages are bumped by setting the `bump_order` property in your package configuration. Packages with lower `bump_order` values will be bumped first.
+
+**Example:**
+
+```toml
+[packages]
+package-a = { path = "packages/a", bump_order = 1 } # Will be bumped first
+package-b = { path = "packages/b", bump_order = 2 } # Will be bumped second
+package-c = { path = "packages/c" } # No explicit order specified
+```
+
+This is particularly useful when you have dependencies between packages and need to ensure they are versioned in a specific order. Tags will be created according to the specified bump order, which can be important for deployment processes or dependency management.
+
+If `bump_order` is not specified for a package, those packages will be processed after packages with explicit ordering.
+
 ### Packages hooks
 
 When creating a monorepo version Cocogitto will execute the pre-bump and post-bump hooks normally. Additionally, it will


Test patch:
diff --git a/tests/lib_tests/bump.rs b/tests/lib_tests/bump.rs
index 94021af..714e8e0 100644
--- a/tests/lib_tests/bump.rs
+++ b/tests/lib_tests/bump.rs
@@ -4,6 +4,7 @@ use std::path::PathBuf;
 
 use anyhow::Result;
 use cmd_lib::run_cmd;
+use cmd_lib::run_fun;
 use sealed_test::prelude::*;
 use speculoos::prelude::*;
 
@@ -273,6 +274,79 @@ fn consecutive_package_bump_ok() -> Result<()> {
     Ok(())
 }
 
+fn ordered_package_bump() -> Result<()> {
+    // Arrange
+    let mut packages = HashMap::new();
+    let jenkins = || MonoRepoPackage {
+        path: PathBuf::from("jenkins"),
+        public_api: false,
+        changelog_path: Some("jenkins/CHANGELOG.md".to_owned()),
+        bump_order: Some(2),
+        ..Default::default()
+    };
+
+    packages.insert("jenkins".to_owned(), jenkins());
+
+    let thumbor = || MonoRepoPackage {
+        path: PathBuf::from("thumbor"),
+        public_api: false,
+        changelog_path: Some("thumbor/CHANGELOG.md".to_owned()),
+        bump_order: Some(1),
+        ..Default::default()
+    };
+
+    packages.insert("thumbor".to_owned(), thumbor());
+
+    let settings = Settings {
+        packages,
+        ignore_merge_commits: true,
+        ..Default::default()
+    };
+
+    let settings = toml::to_string(&settings)?;
+
+    git_init()?;
+    run_cmd!(
+        echo Hello > README.md;
+        git add .;
+        git commit -m "first commit";
+        mkdir jenkins;
+        echo "some jenkins stuff" > jenkins/file;
+        git add .;
+        git commit -m "feat(jenkins): add jenkins stuffs";
+        mkdir thumbor;
+        echo "some thumbor stuff" > thumbor/file;
+        git add .;
+        git commit -m "feat(thumbor): add thumbor stuffs";
+        echo $settings > cog.toml;
+        git add .;
+        git commit -m "chore: add cog.toml";
+    )?;
+
+    let mut cocogitto = CocoGitto::get()?;
+
+    // Act
+    cocogitto.create_monorepo_version(BumpOptions {
+        increment: IncrementCommand::Auto,
+        pre_release: None,
+        build: None,
+        hooks_config: None,
+        annotated: None,
+        dry_run: false,
+        skip_ci: false,
+        skip_ci_override: None,
+        skip_untracked: false,
+        disable_bump_commit: false,
+    });
+
+    // Assert
+    let tags_sorted_by_date = run_fun!(git tag --sort=-taggerdate)?;
+    let tags_sorted_by_date: Vec<&str> = tags_sorted_by_date.lines().collect();
+    assert_that!(tags_sorted_by_date).is_equal_to(vec!["jenkins-0.1.0", "thumbor-0.1.0"]);
+
+    Ok(())
+}
+
 #[sealed_test]
 fn should_fallback_to_0_0_0_when_there_is_no_tag() -> Result<()> {
     // Arrange