diff --git a/git-cliff-core/src/command.rs b/git-cliff-core/src/command.rs index c04a48db0d..22e037f398 100644 --- a/git-cliff-core/src/command.rs +++ b/git-cliff-core/src/command.rs @@ -70,7 +70,6 @@ pub fn run( #[cfg(test)] mod test { - use super::*; #[test] #[cfg(target_family = "unix")] fn run_os_command() -> Result<()> { diff --git a/git-cliff-core/src/config/models_v1.rs b/git-cliff-core/src/config/models_v1.rs index 920be43d20..19713c1452 100644 --- a/git-cliff-core/src/config/models_v1.rs +++ b/git-cliff-core/src/config/models_v1.rs @@ -32,6 +32,7 @@ pub struct Config { } /// Changelog configuration. +#[allow(deprecated)] #[deprecated(since = "3.0.0", note = "deprecated in favor of models_v2")] #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct ChangelogConfig { diff --git a/git-cliff-core/src/config/models_v2.rs b/git-cliff-core/src/config/models_v2.rs index f75148a27c..1788ee5afd 100644 --- a/git-cliff-core/src/config/models_v2.rs +++ b/git-cliff-core/src/config/models_v2.rs @@ -276,11 +276,15 @@ impl Config { release: ReleaseConfig { tags_pattern: config_v1.git.tag_pattern, skip_tags_pattern: config_v1.git.ignore_tags, - order_by: Some(if config_v1.git.topo_order.unwrap() { - TagsOrderBy::Topology - } else { - TagsOrderBy::Time - }), + order_by: Some( + if config_v1.git.topo_order.is_some() && + config_v1.git.topo_order.unwrap() + { + TagsOrderBy::Topology + } else { + TagsOrderBy::Time + }, + ), }, commit: CommitConfig { sort_order: config_v1.git.sort_commits.map( diff --git a/git-cliff-core/src/config/test.rs b/git-cliff-core/src/config/test.rs index 5a986cef07..29e5390fbc 100644 --- a/git-cliff-core/src/config/test.rs +++ b/git-cliff-core/src/config/test.rs @@ -14,7 +14,7 @@ fn parse_config() -> Result<()> { .expect("parent directory not found") .to_path_buf() .join("config") - .join(crate::DEFAULT_CONFIG); + .join(crate::DEFAULT_CONFIG_FILENAME); const FOOTER_VALUE: &str = "test"; const RELEASE_TAGS_PATTERN_VALUE: &str = ".*[0-9].*"; diff --git a/git-cliff-core/src/embed.rs b/git-cliff-core/src/embed.rs index 9707866562..28643c5f70 100644 --- a/git-cliff-core/src/embed.rs +++ b/git-cliff-core/src/embed.rs @@ -18,7 +18,7 @@ pub struct EmbeddedConfig; impl EmbeddedConfig { /// Extracts the embedded content. pub fn get_config() -> Result { - match Self::get(crate::DEFAULT_CONFIG) { + match Self::get(crate::DEFAULT_CONFIG_FILENAME) { Some(v) => Ok(str::from_utf8(&v.data)?.to_string()), None => Err(Error::EmbeddedError(String::from( "Embedded config not found", diff --git a/git-cliff-core/src/lib.rs b/git-cliff-core/src/lib.rs index a2202fe1b6..d445051640 100644 --- a/git-cliff-core/src/lib.rs +++ b/git-cliff-core/src/lib.rs @@ -39,7 +39,7 @@ pub mod template; extern crate log; /// Default configuration file. -pub const DEFAULT_CONFIG: &str = "cliff.toml"; +pub const DEFAULT_CONFIG_FILENAME: &str = "cliff.toml"; /// Default output file. pub const DEFAULT_OUTPUT: &str = "CHANGELOG.md"; /// Default ignore file. diff --git a/git-cliff/src/args.rs b/git-cliff/src/args.rs index 803f8422f9..6140a534f1 100644 --- a/git-cliff/src/args.rs +++ b/git-cliff/src/args.rs @@ -22,7 +22,7 @@ use git_cliff_core::{ TagsOrderBy, }, }, - DEFAULT_CONFIG, + DEFAULT_CONFIG_FILENAME, DEFAULT_OUTPUT, }; use glob::Pattern; @@ -107,11 +107,20 @@ pub struct Opt { long, env = "GIT_CLIFF_CONFIG", value_name = "PATH", - default_value = DEFAULT_CONFIG, + default_value = DEFAULT_CONFIG_FILENAME, value_parser = Opt::parse_dir )] pub config: PathBuf, + /// Sets the version of the configuration file given in `--config`. + #[arg( + long, + env = "GIT_CLIFF_CONFIG_VERSION", + value_name = "VERSION", + default_value_t = 2 + )] + pub config_version: u8, + /// Sets the working directory. #[arg( short, diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index 51f6a8a491..4df3f006ef 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -19,6 +19,8 @@ use args::{ }; use git_cliff_core::changelog::Changelog; use git_cliff_core::commit::Commit; +#[allow(deprecated)] +use git_cliff_core::config::models_v1::Config as Config_v1; use git_cliff_core::config::models_v2::{ CommitParser, CommitSortOrder, @@ -37,7 +39,7 @@ use git_cliff_core::error::{ use git_cliff_core::release::Release; use git_cliff_core::repo::Repository; use git_cliff_core::{ - DEFAULT_CONFIG, + DEFAULT_CONFIG_FILENAME, IGNORE_FILE, }; use std::env; @@ -49,6 +51,7 @@ use std::io::{ self, Write, }; +use std::path::PathBuf; use std::time::{ SystemTime, UNIX_EPOCH, @@ -294,6 +297,62 @@ fn process_repository<'a>( Ok(releases) } +pub fn get_config_path(path: PathBuf) -> PathBuf { + if !path.exists() { + if let Some(config_path) = dirs::config_dir().map(|dir| { + dir.join(env!("CARGO_PKG_NAME")) + .join(DEFAULT_CONFIG_FILENAME) + }) { + return config_path; + } + } + path +} + +/// Loads the configuration based on the given command line arguments. +pub fn load_config(args: &Opt) -> Result { + let config_path = get_config_path(args.config.clone()); + // If the argument `--config` matches the name of a config in + // ./examples, use it. + if let Ok((builtin_config, name)) = + BuiltinConfig::parse(args.config.to_string_lossy().to_string()) + { + info!("Using built-in configuration file {name}."); + return Ok(builtin_config); + } + // If `--config` denotes an existing file, try loading it as configuration. + else if config_path.is_file() { + info!( + "Loading configuration from {}.", + args.config.to_string_lossy() + ); + + // Default to loading a v2 config. + if args.config_version == 2 { + Ok(parsing::parse::(&config_path)?) + } + // Load a v1 config and immediately convert it to v2. + else { + warn!( + "Configuration format v1 is deprecated. Consider migrating to v2. \ + Refer to https://git-cliff.org/docs/configuration/migration for more information." + ); + #[allow(deprecated)] + let config_v1 = parsing::parse::(&config_path)?; + Ok(Config::from(config_v1)) + } + } + // Otherwise fall back to using the embedded configuration from + // ./config/cliff.toml. + else { + warn!( + "{:?} could not be found. Using the default configuration.", + args.config + ); + EmbeddedConfig::parse() + } +} + /// Runs `git-cliff`. pub fn run(mut args: Opt) -> Result<()> { // Check if there is a new version available. @@ -309,16 +368,12 @@ pub fn run(mut args: Opt) -> Result<()> { info!( "Saving the configuration file{} to {:?}", init_config.map(|v| format!(" ({v})")).unwrap_or_default(), - DEFAULT_CONFIG + DEFAULT_CONFIG_FILENAME ); - fs::write(DEFAULT_CONFIG, contents)?; + fs::write(DEFAULT_CONFIG_FILENAME, contents)?; return Ok(()); } - // Retrieve the built-in configuration. - let builtin_config = - BuiltinConfig::parse(args.config.to_string_lossy().to_string()); - // Set the working directory. if let Some(ref workdir) = args.workdir { args.config = workdir.join(args.config); @@ -335,41 +390,8 @@ pub fn run(mut args: Opt) -> Result<()> { } } - // Parse the configuration file. - let mut path = args.config.clone(); - if !path.exists() { - if let Some(config_path) = dirs::config_dir() - .map(|dir| dir.join(env!("CARGO_PKG_NAME")).join(DEFAULT_CONFIG)) - { - path = config_path; - } - } - - // Load the default configuration if necessary. - let mut config = if let Ok((config, name)) = builtin_config { - info!("Using built-in configuration file: {name}"); - config - } else if path.exists() { - parsing::parse(&path)? - } else if let Some(contents) = Config::read_from_manifest()? { - Config::parse_from_str(&contents)? - } else { - if !args.context { - warn!( - "{:?} is not found, using the default configuration.", - args.config - ); - } - EmbeddedConfig::parse()? - }; - if config.changelog.body_template.is_none() && !args.context { - warn!( - "Option `changelog.body_template` is not specified, using the default \ - template." - ); - config.changelog.body_template = - EmbeddedConfig::parse()?.changelog.body_template; - } + // Load the configuration. + let mut config = load_config(&args)?; // Update the configuration based on command line arguments and vice versa. match args.strip {