Repository: cocogitto/cocogitto (rust)

Issue:
[FEATURE] [Monorepo] Add a config attribute to set if a global tag should be generated
Hello,

I have a GitHub repository on which I store Helm Charts and I'm trying to use Cocogitto to manage my Chart's versions.
I made a public repository on which I reproduced my configuration to illustrate the issue I face :
https://github.com/Wassim-AB/cog-test

I have two Helm Charts, Jenkins and Thumbor, that I created in separated commits respectively with the ```feat(jenkins)``` and the ```feat(thumbor)```  prefixes.

Then, I added the Cocogitto configuration file using a ```chore``` commit. Because my repository is only supposed to store the Helm Charts, I set the ```public_api``` attribute of my packages to ```false``` since it doesn't make sense for my repository to have a global tag.

I then executed the ```cog bump --auto --dry-run``` command to see what tags Cocogitto would create, but instead I got this error message :

```
Error: failed to bump version
cause: No conventional commit found to bump current version.
    Only feature, bug fix and breaking change commits will trigger an automatic bump.
```

However, when I remove the ```public_api=false``` attribute of one of my packages, I get this :

```
jenkins-0.1.0
thumbor-0.1.0
0.1.0
```

My supposition is that Cocogitto won't bump packages if they all have the ```public_api=false``` attribute, but it doesn't always make sense for a Monorepo to have a global version since sometimes you just store packages that are related in terms of purpose but not in terms of dependencies.

Is this behavior desired or is it a bug ?

Thank you in advance for your time !
Wassim.

Related feature: https://github.com/cocogitto/cocogitto/issues/232

Pull request:
feat(monorepo): add config opt to disable global global-tag-opt
**Todo** 
-  [x] add some tests

Closes #259

Patch:
diff --git a/.github/workflows/codesee-arch-diagram.yml b/.github/workflows/codesee-arch-diagram.yml
deleted file mode 100644
index 4bada59..0000000
--- a/.github/workflows/codesee-arch-diagram.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-on:
-  push:
-    branches:
-      - main
-  pull_request_target:
-    types: [opened, synchronize, reopened]
-
-name: CodeSee
-
-permissions: read-all
-
-jobs:
-  codesee:
-    runs-on: ubuntu-latest
-    continue-on-error: true
-    name: Analyze the repo with CodeSee
-    steps:
-      - uses: Codesee-io/codesee-action@v2
-        with:
-          codesee-token: ${{ secrets.CODESEE_API_TOKEN }}
diff --git a/cog.toml b/cog.toml
index 2d25d00..ba7d63f 100644
--- a/cog.toml
+++ b/cog.toml
@@ -62,5 +62,6 @@ authors = [
     { signature = "Stephen Connolly", username = "stephenc" },
     { signature = "Luca Trevisani", username = "lucatrv" },
     { signature = "Racci", username = "DaRacci" },
-    { signature = "Conrad Hoffmann", username = "bitfehler" }
+    { signature = "Conrad Hoffmann", username = "bitfehler" },
+    { signature = "andre161292", username = "andre161292" }
 ]
diff --git a/src/command/bump/mod.rs b/src/command/bump/mod.rs
index ecb5a2a..0addb66 100644
--- a/src/command/bump/mod.rs
+++ b/src/command/bump/mod.rs
@@ -24,6 +24,72 @@ mod monorepo;
 mod package;
 mod standard;
 
+struct HookRunOptions<'a> {
+    hook_type: HookType,
+    current_tag: Option<&'a HookVersion>,
+    next_version: Option<&'a HookVersion>,
+    hook_profile: Option<&'a str>,
+    package_name: Option<&'a str>,
+    package: Option<&'a MonoRepoPackage>,
+}
+
+impl<'a> HookRunOptions<'a> {
+    pub fn post_bump() -> Self {
+        Self {
+            hook_type: HookType::PostBump,
+            current_tag: None,
+            next_version: None,
+            hook_profile: None,
+            package_name: None,
+            package: None,
+        }
+    }
+
+    pub fn pre_bump() -> Self {
+        Self {
+            hook_type: HookType::PreBump,
+            current_tag: None,
+            next_version: None,
+            hook_profile: None,
+            package_name: None,
+            package: None,
+        }
+    }
+
+    pub fn current_tag<'b>(mut self, version: Option<&'b HookVersion>) -> Self
+    where
+        'b: 'a,
+    {
+        self.current_tag = version;
+        self
+    }
+
+    pub fn next_version<'b>(mut self, version: &'b HookVersion) -> Self
+    where
+        'b: 'a,
+    {
+        self.next_version = Some(version);
+        self
+    }
+
+    pub fn hook_profile<'b>(mut self, profile: Option<&'b str>) -> Self
+    where
+        'b: 'a,
+    {
+        self.hook_profile = profile;
+        self
+    }
+
+    pub fn package<'b>(mut self, name: &'b str, package: &'b MonoRepoPackage) -> Self
+    where
+        'b: 'a,
+    {
+        self.package_name = Some(name);
+        self.package = Some(package);
+        self
+    }
+}
+
 fn ensure_tag_is_greater_than_previous(current: &Tag, next: &Tag) -> Result<()> {
     if next <= current {
         let comparison = format!("{current} <= {next}").red();
@@ -148,20 +214,12 @@ impl CocoGitto {
         Ok(release)
     }
 
-    fn run_hooks(
-        &self,
-        hook_type: HookType,
-        current_tag: Option<&HookVersion>,
-        next_version: &HookVersion,
-        hook_profile: Option<&str>,
-        package_name: Option<&str>,
-        package: Option<&MonoRepoPackage>,
-    ) -> Result<()> {
+    fn run_hooks(&self, options: HookRunOptions) -> Result<()> {
         let settings = Settings::get(&self.repository)?;
 
-        let hooks: Vec<Hook> = match (package, hook_profile) {
+        let hooks: Vec<Hook> = match (options.package, options.hook_profile) {
             (None, Some(profile)) => settings
-                .get_profile_hooks(profile, hook_type)
+                .get_profile_hooks(profile, options.hook_type)
                 .iter()
                 .map(|s| s.parse())
                 .enumerate()
@@ -173,7 +231,7 @@ impl CocoGitto {
                 .try_collect()?,
 
             (Some(package), Some(profile)) => {
-                let hooks = package.get_profile_hooks(profile, hook_type);
+                let hooks = package.get_profile_hooks(profile, options.hook_type);
 
                 hooks
                     .iter()
@@ -187,14 +245,14 @@ impl CocoGitto {
                     .try_collect()?
             }
             (Some(package), None) => package
-                .get_hooks(hook_type)
+                .get_hooks(options.hook_type)
                 .iter()
                 .map(|s| s.parse())
                 .enumerate()
                 .map(|(idx, result)| result.context(format!("Cannot parse hook at index {idx}")))
                 .try_collect()?,
             (None, None) => settings
-                .get_hooks(hook_type)
+                .get_hooks(options.hook_type)
                 .iter()
                 .map(|s| s.parse())
                 .enumerate()
@@ -203,12 +261,12 @@ impl CocoGitto {
         };
 
         if !hooks.is_empty() {
-            let hook_type = match hook_type {
+            let hook_type = match options.hook_type {
                 HookType::PreBump => "pre-bump",
                 HookType::PostBump => "post-bump",
             };
 
-            match package_name {
+            match options.package_name {
                 None => {
                     let msg = format!("[{hook_type}]").underline().white().bold();
                     info!("{msg}")
@@ -224,7 +282,7 @@ impl CocoGitto {
         }
 
         for mut hook in hooks {
-            hook.insert_versions(current_tag, next_version)?;
+            hook.insert_versions(options.current_tag, options.next_version)?;
             let command = hook.to_string();
             let command = if command.chars().count() > 78 {
                 &command[0..command.len()]
@@ -232,7 +290,7 @@ impl CocoGitto {
                 &command
             };
             info!("[{command}]");
-            let package_path = package.map(|p| p.path.as_path());
+            let package_path = options.package.map(|p| p.path.as_path());
             hook.run(package_path).context(hook.to_string())?;
             println!();
         }
diff --git a/src/command/bump/monorepo.rs b/src/command/bump/monorepo.rs
index 439224c..20d3a0f 100644
--- a/src/command/bump/monorepo.rs
+++ b/src/command/bump/monorepo.rs
@@ -1,4 +1,6 @@
-use crate::command::bump::{ensure_tag_is_greater_than_previous, tag_or_fallback_to_zero};
+use crate::command::bump::{
+    ensure_tag_is_greater_than_previous, tag_or_fallback_to_zero, HookRunOptions,
+};
 
 use crate::conventional::changelog::template::{
     MonoRepoContext, PackageBumpContext, PackageContext,
@@ -9,12 +11,11 @@ use crate::conventional::version::{Increment, IncrementCommand};
 
 use crate::git::tag::Tag;
 use crate::hook::HookVersion;
-use crate::settings::HookType;
 use crate::{settings, CocoGitto, SETTINGS};
 use anyhow::Result;
 use colored::*;
 
-use log::info;
+use log::{info, warn};
 use semver::Prerelease;
 use tera::Tera;
 
@@ -47,7 +48,14 @@ impl CocoGitto {
     ) -> Result<()> {
         match increment {
             IncrementCommand::Auto => {
-                self.create_monorepo_version_auto(pre_release, hooks_config, annotated, dry_run)
+                if SETTINGS.generate_mono_repository_global_tag {
+                    self.create_monorepo_version_auto(pre_release, hooks_config, annotated, dry_run)
+                } else {
+                    if annotated.is_some() {
+                        warn!("--annotated flag is not supported for package bumps without a global tag");
+                    }
+                    self.create_all_package_version_auto(pre_release, hooks_config, dry_run)
+                }
             }
             _ => self.create_monorepo_version_manual(
                 increment,
@@ -59,6 +67,59 @@ impl CocoGitto {
         }
     }
 
+    pub fn create_all_package_version_auto(
+        &mut self,
+        pre_release: Option<&str>,
+        hooks_config: Option<&str>,
+        dry_run: bool,
+    ) -> Result<()> {
+        self.pre_bump_checks()?;
+        // Get package bumps
+        let bumps = self.get_packages_bumps(pre_release)?;
+
+        if dry_run {
+            for bump in bumps {
+                println!("{}", bump.new_version.prefixed_tag)
+            }
+            return Ok(());
+        }
+
+        let hook_result = self.run_hooks(HookRunOptions::pre_bump().hook_profile(hooks_config));
+
+        self.repository.add_all()?;
+        self.unwrap_or_stash_and_exit(&Tag::default(), hook_result);
+        self.bump_packages(pre_release, hooks_config, &bumps)?;
+
+        let sign = self.repository.gpg_sign();
+        self.repository
+            .commit("chore(version): bump packages", sign)?;
+
+        for bump in &bumps {
+            self.repository.create_tag(&bump.new_version.prefixed_tag)?;
+        }
+
+        // Run per package post hooks
+        for bump in bumps {
+            let package = SETTINGS
+                .packages
+                .get(&bump.package_name)
+                .expect("package exists");
+
+            self.run_hooks(
+                HookRunOptions::post_bump()
+                    .current_tag(bump.old_version.as_ref())
+                    .next_version(&bump.new_version)
+                    .hook_profile(hooks_config)
+                    .package(&bump.package_name, package),
+            )?;
+        }
+
+        // Run global post hooks
+        self.run_hooks(HookRunOptions::post_bump().hook_profile(hooks_config))?;
+
+        Ok(())
+    }
+
     fn create_monorepo_version_auto(
         &mut self,
         pre_release: Option<&str>,
@@ -143,12 +204,10 @@ impl CocoGitto {
         let next_version = HookVersion::new(tag.clone());
 
         let hook_result = self.run_hooks(
-            HookType::PreBump,
-            current.as_ref(),
-            &next_version,
-            hooks_config,
-            None,
-            None,
+            HookRunOptions::pre_bump()
+                .current_tag(current.as_ref())
+                .next_version(&next_version)
+                .hook_profile(hooks_config),
         );
 
         self.repository.add_all()?;
@@ -184,23 +243,20 @@ impl CocoGitto {
                 .get(&bump.package_name)
                 .expect("package exists");
             self.run_hooks(
-                HookType::PostBump,
-                bump.old_version.as_ref(),
-                &bump.new_version,
-                hooks_config,
-                Some(&bump.package_name),
-                Some(package),
+                HookRunOptions::post_bump()
+                    .current_tag(bump.old_version.as_ref())
+                    .next_version(&bump.new_version)
+                    .hook_profile(hooks_config)
+                    .package(&bump.package_name, package),
             )?;
         }
 
         // Run global post hooks
         self.run_hooks(
-            HookType::PostBump,
-            current.as_ref(),
-            &next_version,
-            hooks_config,
-            None,
-            None,
+            HookRunOptions::post_bump()
+                .current_tag(current.as_ref())
+                .next_version(&next_version)
+                .hook_profile(hooks_config),
         )?;
 
         Ok(())
@@ -267,12 +323,10 @@ impl CocoGitto {
         let next_version = HookVersion::new(tag.clone());
 
         let hook_result = self.run_hooks(
-            HookType::PreBump,
-            current.as_ref(),
-            &next_version,
-            hooks_config,
-            None,
-            None,
+            HookRunOptions::pre_bump()
+                .current_tag(current.as_ref())
+                .next_version(&next_version)
+                .hook_profile(hooks_config),
         );
 
         self.repository.add_all()?;
@@ -296,12 +350,10 @@ impl CocoGitto {
 
         // Run global post hooks
         self.run_hooks(
-            HookType::PostBump,
-            current.as_ref(),
-            &next_version,
-            hooks_config,
-            None,
-            None,
+            HookRunOptions::post_bump()
+                .current_tag(current.as_ref())
+                .next_version(&next_version)
+                .hook_profile(hooks_config),
         )?;
 
         Ok(())
@@ -432,12 +484,11 @@ impl CocoGitto {
             let new_version = HookVersion::new(tag.clone());
 
             let hook_result = self.run_hooks(
-                HookType::PreBump,
-                old_version.as_ref(),
-                &new_version,
-                hooks_config,
-                Some(package_name),
-                Some(package),
+                HookRunOptions::pre_bump()
+                    .current_tag(old_version.as_ref())
+                    .next_version(&new_version)
+                    .hook_profile(hooks_config)
+                    .package(package_name, package),
             );
 
             self.repository.add_all()?;
diff --git a/src/command/bump/package.rs b/src/command/bump/package.rs
index 1472ef2..99515af 100644
--- a/src/command/bump/package.rs
+++ b/src/command/bump/package.rs
@@ -1,10 +1,12 @@
-use crate::command::bump::{ensure_tag_is_greater_than_previous, tag_or_fallback_to_zero};
+use crate::command::bump::{
+    ensure_tag_is_greater_than_previous, tag_or_fallback_to_zero, HookRunOptions,
+};
 use crate::conventional::changelog::template::PackageContext;
 use crate::conventional::changelog::ReleaseType;
 use crate::conventional::version::IncrementCommand;
 use crate::git::tag::Tag;
 use crate::hook::HookVersion;
-use crate::settings::{HookType, MonoRepoPackage};
+use crate::settings::MonoRepoPackage;
 use crate::{CocoGitto, SETTINGS};
 use anyhow::Result;
 use colored::*;
@@ -63,12 +65,11 @@ impl CocoGitto {
         ));
 
         let hook_result = self.run_hooks(
-            HookType::PreBump,
-            current.as_ref(),
-            &next_version,
-            hooks_config,
-            Some(package_name),
-            Some(package),
+            HookRunOptions::pre_bump()
+                .current_tag(current.as_ref())
+                .next_version(&next_version)
+                .hook_profile(hooks_config)
+                .package(package_name, package),
         );
 
         self.repository.add_all()?;
@@ -89,12 +90,11 @@ impl CocoGitto {
         }
 
         self.run_hooks(
-            HookType::PostBump,
-            current.as_ref(),
-            &next_version,
-            hooks_config,
-            Some(package_name),
-            Some(package),
+            HookRunOptions::post_bump()
+                .current_tag(current.as_ref())
+                .next_version(&next_version)
+                .hook_profile(hooks_config)
+                .package(package_name, package),
         )?;
 
         let current = current
diff --git a/src/command/bump/standard.rs b/src/command/bump/standard.rs
index ede3cfd..caab7a0 100644
--- a/src/command/bump/standard.rs
+++ b/src/command/bump/standard.rs
@@ -1,10 +1,11 @@
-use crate::command::bump::{ensure_tag_is_greater_than_previous, tag_or_fallback_to_zero};
+use crate::command::bump::{
+    ensure_tag_is_greater_than_previous, tag_or_fallback_to_zero, HookRunOptions,
+};
 
 use crate::conventional::changelog::ReleaseType;
 use crate::conventional::version::IncrementCommand;
 use crate::git::tag::Tag;
 use crate::hook::HookVersion;
-use crate::settings::HookType;
 use crate::{settings, CocoGitto, SETTINGS};
 use anyhow::Result;
 use colored::*;
@@ -54,12 +55,10 @@ impl CocoGitto {
         let next_version = HookVersion::new(tag.clone());
 
         let hook_result = self.run_hooks(
-            HookType::PreBump,
-            current.as_ref(),
-            &next_version,
-            hooks_config,
-            None,
-            None,
+            HookRunOptions::pre_bump()
+                .current_tag(current.as_ref())
+                .next_version(&next_version)
+                .hook_profile(hooks_config),
         );
 
         self.repository.add_all()?;
@@ -83,12 +82,10 @@ impl CocoGitto {
         }
 
         self.run_hooks(
-            HookType::PostBump,
-            current.as_ref(),
-            &next_version,
-            hooks_config,
-            None,
-            None,
+            HookRunOptions::post_bump()
+                .current_tag(current.as_ref())
+                .next_version(&next_version)
+                .hook_profile(hooks_config),
         )?;
 
         let current = current
diff --git a/src/hook/mod.rs b/src/hook/mod.rs
index f977747..0ad2722 100644
--- a/src/hook/mod.rs
+++ b/src/hook/mod.rs
@@ -31,15 +31,16 @@ impl HookVersion {
 impl VersionSpan {
     pub(crate) fn build_version_str(
         &mut self,
-        version: &HookVersion,
+        version: Option<&HookVersion>,
         latest: Option<&HookVersion>,
     ) -> Result<String> {
-        let version = version.prefixed_tag.version.clone();
+        let version = version.map(|version| version.prefixed_tag.version.clone());
         let latest = latest.map(|version| version.prefixed_tag.version.clone());
 
         // According to the pest grammar, a `version` or `latest_version` token is expected first
         let mut version = match self.tokens.pop_front() {
-            Some(Token::Version) => Ok(version),
+            Some(Token::Version) => version
+                .ok_or_else(|| anyhow!("No previous tag found to replace {{version}} version")),
             Some(Token::LatestVersion) => {
                 latest.ok_or_else(|| anyhow!("No previous tag found to replace {{latest}} version"))
             }
@@ -85,7 +86,7 @@ pub struct HookSpan {
 impl HookSpan {
     fn replace_versions(
         &mut self,
-        version: &HookVersion,
+        version: Option<&HookVersion>,
         latest: Option<&HookVersion>,
     ) -> Result<String> {
         let mut output = self.content.clone();
@@ -122,7 +123,7 @@ impl Hook {
     pub(crate) fn insert_versions(
         &mut self,
         current_version: Option<&HookVersion>,
-        next_version: &HookVersion,
+        next_version: Option<&HookVersion>,
     ) -> Result<()> {
         let mut parts = parser::parse(&self.0)?;
         self.0 = parts.replace_versions(next_version, current_version)?;
@@ -169,7 +170,7 @@ mod test {
     #[test]
     fn replace_version_cargo() -> Result<()> {
         let mut hook = Hook::from_str("cargo bump {{version}}")?;
-        hook.insert_versions(None, &HookVersion::new(Tag::from_str("1.0.0", None)?))
+        hook.insert_versions(None, Some(&HookVersion::new(Tag::from_str("1.0.0", None)?)))
             .unwrap();
 
         assert_that!(hook.0.as_str()).is_equal_to("cargo bump 1.0.0");
@@ -179,7 +180,7 @@ mod test {
     #[test]
     fn replace_maven_version() -> Result<()> {
         let mut hook = Hook::from_str("mvn versions:set -DnewVersion={{version}}")?;
-        hook.insert_versions(None, &HookVersion::new(Tag::from_str("1.0.0", None)?))
+        hook.insert_versions(None, Some(&HookVersion::new(Tag::from_str("1.0.0", None)?)))
             .unwrap();
 
         assert_that!(hook.0.as_str()).is_equal_to("mvn versions:set -DnewVersion=1.0.0");
@@ -189,7 +190,7 @@ mod test {
     #[test]
     fn replace_maven_version_with_expression() -> Result<()> {
         let mut hook = Hook::from_str("mvn versions:set -DnewVersion={{version+1minor-SNAPSHOT}}")?;
-        hook.insert_versions(None, &HookVersion::new(Tag::from_str("1.0.0", None)?))
+        hook.insert_versions(None, Some(&HookVersion::new(Tag::from_str("1.0.0", None)?)))
             .unwrap();
 
         assert_that!(hook.0.as_str()).is_equal_to("mvn versions:set -DnewVersion=1.1.0-SNAPSHOT");
@@ -199,7 +200,7 @@ mod test {
     #[test]
     fn leave_hook_untouched_when_no_version() -> Result<()> {
         let mut hook = Hook::from_str("echo \"Hello World\"")?;
-        hook.insert_versions(None, &HookVersion::new(Tag::from_str("1.0.0", None)?))
+        hook.insert_versions(None, Some(&HookVersion::new(Tag::from_str("1.0.0", None)?)))
             .unwrap();
 
         assert_that!(hook.0.as_str()).is_equal_to("echo \"Hello World\"");
@@ -209,7 +210,7 @@ mod test {
     #[test]
     fn replace_quoted_version() -> Result<()> {
         let mut hook = Hook::from_str("echo \"{{version}}\"")?;
-        hook.insert_versions(None, &HookVersion::new(Tag::from_str("1.0.0", None)?))
+        hook.insert_versions(None, Some(&HookVersion::new(Tag::from_str("1.0.0", None)?)))
             .unwrap();
 
         assert_that!(hook.0.as_str()).is_equal_to("echo \"1.0.0\"");
@@ -220,7 +221,7 @@ mod test {
     fn replace_version_with_nested_simple_quoted_arg() -> Result<()> {
         let mut hook =
             Hook::from_str("cog commit chore 'bump snapshot to {{version+1minor-pre}}'")?;
-        hook.insert_versions(None, &HookVersion::new(Tag::from_str("1.0.0", None)?))
+        hook.insert_versions(None, Some(&HookVersion::new(Tag::from_str("1.0.0", None)?)))
             .unwrap();
 
         assert_that!(hook.0.as_str()).is_equal_to("cog commit chore 'bump snapshot to 1.1.0-pre'");
@@ -231,7 +232,7 @@ mod test {
     fn replace_version_with_nested_double_quoted_arg() -> Result<()> {
         let mut hook =
             Hook::from_str("cog commit chore \"bump snapshot to {{version+1minor-pre}}\"")?;
-        hook.insert_versions(None, &HookVersion::new(Tag::from_str("1.0.0", None)?))
+        hook.insert_versions(None, Some(&HookVersion::new(Tag::from_str("1.0.0", None)?)))
             .unwrap();
 
         assert_that!(hook.0.as_str())
@@ -244,7 +245,7 @@ mod test {
         let mut hook = Hook::from_str("echo \"the latest {{latest}}, the greatest {{version}}\"")?;
         hook.insert_versions(
             Some(&HookVersion::new(Tag::from_str("0.5.9", None)?)),
-            &HookVersion::new(Tag::from_str("1.0.0", None)?),
+            Some(&HookVersion::new(Tag::from_str("1.0.0", None)?)),
         )
         .unwrap();
 
@@ -259,7 +260,7 @@ mod test {
         )?;
         hook.insert_versions(
             Some(&HookVersion::new(Tag::from_str("0.5.9", None)?)),
-            &HookVersion::new(Tag::from_str("1.0.0", None)?),
+            Some(&HookVersion::new(Tag::from_str("1.0.0", None)?)),
         )
         .unwrap();
 
@@ -271,7 +272,7 @@ mod test {
     fn replace_version_with_pre_and_build_metadata() -> Result<()> {
         let mut hook =
             Hook::from_str("echo \"the latest {{version+1major-pre.alpha-bravo+build.42}}\"")?;
-        hook.insert_versions(None, &HookVersion::new(Tag::from_str("1.0.0", None)?))
+        hook.insert_versions(None, Some(&HookVersion::new(Tag::from_str("1.0.0", None)?)))
             .unwrap();
 
         assert_that!(hook.0.as_str())
@@ -285,7 +286,7 @@ mod test {
 
         let mut hook = Hook::from_str("git commit --allow-empty -m 'chore(snapshot): bump snapshot to {{version+1patch-SNAPSHOT}}'")?;
 
-        hook.insert_versions(None, &HookVersion::new(Tag::from_str("1.0.0", None)?))
+        hook.insert_versions(None, Some(&HookVersion::new(Tag::from_str("1.0.0", None)?)))
             .unwrap();
 
         let outcome = hook.run(None);
diff --git a/src/settings/mod.rs b/src/settings/mod.rs
index 8ba0602..65916d2 100644
--- a/src/settings/mod.rs
+++ b/src/settings/mod.rs
@@ -25,36 +25,46 @@ pub enum HookType {
     PostBump,
 }
 
-#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Default)]
-#[serde(deny_unknown_fields)]
+#[derive(Debug, Deserialize, Serialize, Eq, PartialEq)]
+#[serde(deny_unknown_fields, default)]
 pub struct Settings {
-    #[serde(default)]
     pub from_latest_tag: bool,
-    #[serde(default)]
     pub ignore_merge_commits: bool,
-    #[serde(default)]
+    pub generate_mono_repository_global_tag: bool,
     pub monorepo_version_separator: Option<String>,
-    #[serde(default)]
     pub branch_whitelist: Vec<String>,
     pub tag_prefix: Option<String>,
-    #[serde(default)]
     pub pre_bump_hooks: Vec<String>,
-    #[serde(default)]
     pub post_bump_hooks: Vec<String>,
-    #[serde(default)]
     pub pre_package_bump_hooks: Vec<String>,
-    #[serde(default)]
     pub post_package_bump_hooks: Vec<String>,
-    #[serde(default)]
     pub commit_types: CommitsMetadataSettings,
-    #[serde(default)]
     pub changelog: Changelog,
-    #[serde(default)]
     pub bump_profiles: HashMap<String, BumpProfile>,
-    #[serde(default)]
     pub packages: HashMap<String, MonoRepoPackage>,
 }
 
+impl Default for Settings {
+    fn default() -> Self {
+        Self {
+            from_latest_tag: false,
+            ignore_merge_commits: false,
+            generate_mono_repository_global_tag: true,
+            monorepo_version_separator: None,
+            branch_whitelist: vec![],
+            tag_prefix: None,
+            pre_bump_hooks: vec![],
+            post_bump_hooks: vec![],
+            pre_package_bump_hooks: vec![],
+            post_package_bump_hooks: vec![],
+            commit_types: Default::default(),
+            changelog: Default::default(),
+            bump_profiles: Default::default(),
+            packages: Default::default(),
+        }
+    }
+}
+
 #[derive(Debug, Deserialize, Serialize, Eq, PartialEq)]
 #[serde(deny_unknown_fields, default)]
 pub struct MonoRepoPackage {


Test patch:
diff --git a/tests/lib_tests/bump.rs b/tests/lib_tests/bump.rs
index 2a72c2f..5ba1eec 100644
--- a/tests/lib_tests/bump.rs
+++ b/tests/lib_tests/bump.rs
@@ -78,7 +78,81 @@ fn monorepo_bump_ok() -> Result<()> {
 }
 
 #[sealed_test]
-fn monorepo_bump_issue_262_ok() -> Result<()> {
+fn monorepo_bump_manual_ok() -> Result<()> {
+    // Arrange
+    let mut settings = Settings {
+        ..Default::default()
+    };
+
+    init_monorepo(&mut settings)?;
+    run_cmd!(
+        git tag "one-0.1.0";
+    )?;
+
+    let mut cocogitto = CocoGitto::get()?;
+
+    // Act
+    let result =
+        cocogitto.create_monorepo_version(IncrementCommand::Major, None, None, None, false);
+
+    // Assert
+    assert_that!(result).is_ok();
+    assert_tag_exists("1.0.0")?;
+    Ok(())
+}
+
+#[sealed_test]
+fn monorepo_with_tag_prefix_bump_ok() -> Result<()> {
+    // Arrange
+    let mut settings = Settings {
+        tag_prefix: Some("v".to_string()),
+        ..Default::default()
+    };
+
+    init_monorepo(&mut settings)?;
+
+    let mut cocogitto = CocoGitto::get()?;
+
+    // Act
+    let result = cocogitto.create_monorepo_version(IncrementCommand::Auto, None, None, None, false);
+
+    // Assert
+    assert_that!(result).is_ok();
+    assert_tag_exists("v0.1.0")?;
+    assert_tag_exists("one-v0.1.0")?;
+    Ok(())
+}
+
+#[sealed_test]
+fn package_bump_ok() -> Result<()> {
+    // Arrange
+    let mut settings = Settings {
+        ..Default::default()
+    };
+
+    init_monorepo(&mut settings)?;
+    let package = settings.packages.get("one").unwrap();
+    let mut cocogitto = CocoGitto::get()?;
+
+    // Act
+    let result = cocogitto.create_package_version(
+        ("one", package),
+        IncrementCommand::AutoPackage("one".to_string()),
+        None,
+        None,
+        None,
+        false,
+    );
+
+    // Assert
+    assert_that!(result).is_ok();
+    assert_tag_does_not_exist("0.1.0")?;
+    assert_tag_exists("one-0.1.0")?;
+    Ok(())
+}
+
+#[sealed_test]
+fn consecutive_package_bump_ok() -> Result<()> {
     // Arrange
     let mut packages = HashMap::new();
     let jenkins = || MonoRepoPackage {
@@ -161,8 +235,6 @@ fn monorepo_bump_issue_262_ok() -> Result<()> {
         false,
     )?;
 
-    run_cmd!(git --no-pager log;)?;
-
     // Assert
     assert_tag_exists("jenkins-0.1.0")?;
     assert_tag_exists("thumbor-0.1.0")?;
@@ -173,94 +245,90 @@ fn monorepo_bump_issue_262_ok() -> Result<()> {
 }
 
 #[sealed_test]
-fn monorepo_bump_manual_ok() -> Result<()> {
+fn should_fallback_to_0_0_0_when_there_is_no_tag() -> Result<()> {
     // Arrange
-    let mut settings = Settings {
-        ..Default::default()
-    };
-
-    init_monorepo(&mut settings)?;
-    run_cmd!(
-        git tag "one-0.1.0";
-    )?;
+    git_init()?;
+    git_commit("chore: first commit")?;
+    git_commit("feat: add a feature commit")?;
 
     let mut cocogitto = CocoGitto::get()?;
 
     // Act
-    let result =
-        cocogitto.create_monorepo_version(IncrementCommand::Major, None, None, None, false);
+    let result = cocogitto.create_version(IncrementCommand::Auto, None, None, None, false);
 
     // Assert
     assert_that!(result).is_ok();
-    assert_tag_exists("1.0.0")?;
+    assert_latest_tag("0.1.0")?;
     Ok(())
 }
 
 #[sealed_test]
-fn monorepo_with_tag_prefix_bump_ok() -> Result<()> {
+fn auto_bump_package_only_ok() -> Result<()> {
     // Arrange
-    let mut settings = Settings {
-        tag_prefix: Some("v".to_string()),
+    let mut packages = HashMap::new();
+    let jenkins = || MonoRepoPackage {
+        path: PathBuf::from("jenkins"),
+        public_api: false,
+        changelog_path: Some("jenkins/CHANGELOG.md".to_owned()),
         ..Default::default()
     };
 
-    init_monorepo(&mut settings)?;
-
-    let mut cocogitto = CocoGitto::get()?;
+    packages.insert("jenkins".to_owned(), jenkins());
 
-    // Act
-    let result = cocogitto.create_monorepo_version(IncrementCommand::Auto, None, None, None, false);
+    let thumbor = || MonoRepoPackage {
+        path: PathBuf::from("thumbor"),
+        public_api: false,
+        changelog_path: Some("thumbor/CHANGELOG.md".to_owned()),
+        ..Default::default()
+    };
 
-    // Assert
-    assert_that!(result).is_ok();
-    assert_tag_exists("v0.1.0")?;
-    assert_tag_exists("one-v0.1.0")?;
-    Ok(())
-}
+    packages.insert("thumbor".to_owned(), thumbor());
 
-#[sealed_test]
-fn package_bump_ok() -> Result<()> {
-    // Arrange
-    let mut settings = Settings {
+    let settings = Settings {
+        packages,
+        generate_mono_repository_global_tag: false,
         ..Default::default()
     };
 
-    init_monorepo(&mut settings)?;
-    let package = settings.packages.get("one").unwrap();
+    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
-    let result = cocogitto.create_package_version(
-        ("one", package),
-        IncrementCommand::AutoPackage("one".to_string()),
-        None,
-        None,
-        None,
-        false,
-    );
+    cocogitto.create_all_package_version_auto(None, None, false)?;
 
-    // Assert
-    assert_that!(result).is_ok();
+    assert_tag_exists("jenkins-0.1.0")?;
+    assert_tag_exists("thumbor-0.1.0")?;
     assert_tag_does_not_exist("0.1.0")?;
-    assert_tag_exists("one-0.1.0")?;
-    Ok(())
-}
-
-#[sealed_test]
-fn should_fallback_to_0_0_0_when_there_is_no_tag() -> Result<()> {
-    // Arrange
-    git_init()?;
-    git_commit("chore: first commit")?;
-    git_commit("feat: add a feature commit")?;
 
-    let mut cocogitto = CocoGitto::get()?;
+    run_cmd!(
+        echo "fix jenkins bug" > jenkins/fix;
+        git add .;
+        git commit -m "fix(jenkins): bug fix on jenkins package";
+    )?;
 
-    // Act
-    let result = cocogitto.create_version(IncrementCommand::Auto, None, None, None, false);
+    cocogitto.create_all_package_version_auto(None, None, false)?;
 
     // Assert
-    assert_that!(result).is_ok();
-    assert_latest_tag("0.1.0")?;
+    assert_tag_exists("jenkins-0.1.1")?;
     Ok(())
 }