From 25121ec6a8a7b0072b14dd028be36390a0839c84 Mon Sep 17 00:00:00 2001 From: Christopher Cyclonit Klinge Date: Tue, 19 Mar 2024 07:22:56 +0100 Subject: [PATCH] feature(config): add migrate-config subcommand Added a subcommand migrate-config to migrate from the old to the new configuration format. --- __TODO.toml | 78 +++++++++++++++++++ git-cliff-core/src/config/migrate.rs | 38 +++++++++ git-cliff-core/src/config/mod.rs | 2 + git-cliff/src/args.rs | 111 ++++++++++++++++++--------- git-cliff/src/main.rs | 19 ++++- 5 files changed, 211 insertions(+), 37 deletions(-) create mode 100644 __TODO.toml create mode 100644 git-cliff-core/src/config/migrate.rs diff --git a/__TODO.toml b/__TODO.toml new file mode 100644 index 0000000000..54e6c602df --- /dev/null +++ b/__TODO.toml @@ -0,0 +1,78 @@ +>>> [changelog] +>>> # A static header for the changelog. +>>> header Option // changelog.header +>>> +>>> # A Tera template to be rendered for each release in the changelog (see https://keats.github.io/tera/docs/#introduction). +>>> body_template Option // changelog.body +>>> +>>> # A Tera template to be rendered as the changelog's footer (see https://keats.github.io/tera/docs/#introduction). +>>> footer_template Option // changelog.footer +>>> +>>> # Whether to remove leading and trailing whitespaces from all lines of the changelog's body. +>>> trim_body_whitespace Option // changelog.trim + +>>> # A list of postprocessors using regex to modify the changelog. +>>> postprocessors Option> +>>> +>>> # Whether to exclude changes that do not belong to any group from the changelog. +>>> exclude_ungrouped_changes Option // git.filter_commits + + +>>> [release] +>>> # Regex to select git tags that represent releases. +>>> # Example: "v[0-9].*" +>>> tags_pattern String // git.tag_pattern +>>> +>>> # Regex to select git tags that do not represent proper releases. Takes precedence over `release.tags_pattern`. +>>> # Changes belonging to these releases will be included in the next non-skipped release. +>>> # Example: "rc" +>>> skip_tags_pattern String // git.ignore_tags +>>> +>>> # Whether to order releases chronologically or topologically. +>>> # Must be either `time` or `topology`. +>>> order_by Enum: "time" / "topology" // git.topo_order + + +>>> [commit] +>>> # Whether to order commits newest to oldest or oldest to newest in their group. +>>> # Must be either `newest` or `oldest`. +>>> sort_order Enum: "newest" / "oldest" // git.sort_commits +>>> +>>> # Whether to limit the total number of commits to be included in the changelog. +>>> max_commit_count Option // git.limit_commits +>>> +>>> # Whether to split commits on newlines, treating each line as an individual commit. +>>> split_by_newline Option // git.split_commits +>>> +>>> # Regex to select git tags that should be excluded from the changelog. +>>> exclude_tags_pattern String // git.skip_tags +>>> +>>> # A list of preprocessors to modify commit messages using regex prior to further processing. +>>> message_preprocessors Option> // git.commit_preprocessors +>>> +>>> # A list of parsers using regex for extracting external references found in commit messages, and turning them into links. The gemerated links can be used in the body template as `commit.links`. +>>> # Example: "RFC(\\d+)" -> "https://datatracker.ietf.org/doc/html/rfc$1" +>>> link_parsers Option> // git.link_parsers +>>> +>>> # Whether to parse commits according to the conventional commits specification. +>>> # Sets the commits' `group` (= `type`), `scope`, `message` (= `description`), `body`, `breaking`, `breaking_description` and `footers`. +>>> parse_conventional_commits Option // git.conventional_commits + +# Whether to fail generating the changelog if the history contains commits that do not match the conventional commits specification. +require_conventional_commits Option + +>>> # Whether to exclude commits that do not match the conventional commits specification from the changelog. +>>> exclude_unconventional_commits Option // git.filter_unconventional +>>> +>>> # A list of parsers using regex for extracting data from the commit message. +>>> # Sets the commits' `group` and `scope` and can decide to exclude commits from further processing. +>>> commit_parsers Option> // git.commit_parsers +>>> +>>> # Whether to prevent breaking changes from being excluded by commit parsers. +>>> retain_breaking_changes Option // git.protect_breaking_commits + + +[remote.github] +owner String +repo String +token Option \ No newline at end of file diff --git a/git-cliff-core/src/config/migrate.rs b/git-cliff-core/src/config/migrate.rs new file mode 100644 index 0000000000..7ed67829ea --- /dev/null +++ b/git-cliff-core/src/config/migrate.rs @@ -0,0 +1,38 @@ +use crate::error::{ + Error, + Result, +}; +use clap::Args; +use std::path::PathBuf; + +/// Migrates configuration files from the old to the new schema. +#[derive(Args, Debug)] +pub struct MigrateArgs { + /// The file to read the original configuration from. + #[arg(long = "in")] + pub in_path: PathBuf, + + /// The file to write the migrated configuration to. + #[arg(long = "out")] + pub out_path: PathBuf, +} + +/// Migrates configuration files from the old to the new schema. +pub fn run(args: &MigrateArgs) -> Result<()> { + // load the old configuration + if !args.in_path.exists() { + return Err(Error::ArgumentError(String::from( + "File {0} does not exist.", + ))); + } + let old_config = + match super::parsing::parse::(&args.in_path) { + Ok(c) => c, + Err(e) => return Err(e), + }; + + let new_config = super::models_v2::Config::from(old_config); + info!("{:?}", new_config); + + Ok(()) +} diff --git a/git-cliff-core/src/config/mod.rs b/git-cliff-core/src/config/mod.rs index 623c11366d..c3bdbaf973 100644 --- a/git-cliff-core/src/config/mod.rs +++ b/git-cliff-core/src/config/mod.rs @@ -1,3 +1,5 @@ +/// Provide a command to migrate from old to new configuration. +pub mod migrate; /// Deprecated Config models for git-cliff. pub mod models_v1; /// Current Config models for git-cliff. diff --git a/git-cliff/src/args.rs b/git-cliff/src/args.rs index b7d75df61c..803f8422f9 100644 --- a/git-cliff/src/args.rs +++ b/git-cliff/src/args.rs @@ -10,13 +10,17 @@ use clap::{ }, ArgAction, Parser, + Subcommand, ValueEnum, }; use git_cliff_core::{ - config::models_v2::{ - CommitSortOrder, - Remote, - TagsOrderBy, + config::{ + migrate::MigrateArgs, + models_v2::{ + CommitSortOrder, + Remote, + TagsOrderBy, + }, }, DEFAULT_CONFIG, DEFAULT_OUTPUT, @@ -33,6 +37,11 @@ pub enum Strip { All, } +#[derive(Debug, Subcommand)] +pub enum SubCommands { + MigrateConfig(MigrateArgs), +} + /// Command-line arguments to parse. #[derive(Debug, Parser)] #[command( @@ -40,7 +49,7 @@ pub enum Strip { author = clap::crate_authors!("\n"), about, rename_all_env = "screaming-snake", - help_template = "\ + help_template = "\ {before-help}{name} {version} {author-with-newline}{about-with-newline} {usage-heading} @@ -50,10 +59,14 @@ pub enum Strip { ", override_usage = "git-cliff [FLAGS] [OPTIONS] [--] [RANGE]", next_help_heading = Some("OPTIONS"), - disable_help_flag = true, - disable_version_flag = true, + disable_help_flag = true, + disable_version_flag = true, + propagate_version = true, )] pub struct Opt { + #[command(subcommand)] + pub subcommand: Option, + #[arg( short, long, @@ -62,7 +75,8 @@ pub struct Opt { help = "Prints help information", help_heading = "FLAGS" )] - pub help: Option, + pub help: Option, + #[arg( short = 'V', long, @@ -71,10 +85,12 @@ pub struct Opt { help = "Prints version information", help_heading = "FLAGS" )] - pub version: Option, + pub version: Option, + /// Increases the logging verbosity. #[arg(short, long, action = ArgAction::Count, alias = "debug", help_heading = Some("FLAGS"))] - pub verbose: u8, + pub verbose: u8, + /// Writes the default configuration file to cliff.toml #[arg( short, @@ -83,7 +99,8 @@ pub struct Opt { num_args = 0..=1, required = false )] - pub init: Option>, + pub init: Option>, + /// Sets the configuration file. #[arg( short, @@ -93,7 +110,8 @@ pub struct Opt { default_value = DEFAULT_CONFIG, value_parser = Opt::parse_dir )] - pub config: PathBuf, + pub config: PathBuf, + /// Sets the working directory. #[arg( short, @@ -102,7 +120,8 @@ pub struct Opt { value_name = "PATH", value_parser = Opt::parse_dir )] - pub workdir: Option, + pub workdir: Option, + /// Sets the git repository. #[arg( short, @@ -112,7 +131,8 @@ pub struct Opt { num_args(1..), value_parser = Opt::parse_dir )] - pub repository: Option>, + pub repository: Option>, + /// Sets the path to include related commits. #[arg( long, @@ -120,7 +140,8 @@ pub struct Opt { value_name = "PATTERN", num_args(1..) )] - pub include_path: Option>, + pub include_path: Option>, + /// Sets the path to exclude related commits. #[arg( long, @@ -128,10 +149,12 @@ pub struct Opt { value_name = "PATTERN", num_args(1..) )] - pub exclude_path: Option>, + pub exclude_path: Option>, + /// Sets the regex to select git tags that represent releases. #[arg(long, env = "GIT_CLIFF_RELEASE_TAGS_PATTERN", value_name = "PATTERN")] pub release_tags_pattern: Option, + /// Sets custom commit messages to include in the changelog. #[arg( long, @@ -139,7 +162,8 @@ pub struct Opt { value_name = "MSG", num_args(1..) )] - pub with_commit: Option>, + pub with_commit: Option>, + /// Sets commits that will be skipped in the changelog. #[arg( long, @@ -147,7 +171,8 @@ pub struct Opt { value_name = "SHA1", num_args(1..) )] - pub skip_commit: Option>, + pub skip_commit: Option>, + /// Prepends entries to the given changelog file. #[arg( short, @@ -156,7 +181,8 @@ pub struct Opt { value_name = "PATH", value_parser = Opt::parse_dir )] - pub prepend: Option, + pub prepend: Option, + /// Writes output to the given file. #[arg( short, @@ -167,7 +193,8 @@ pub struct Opt { num_args = 0..=1, default_missing_value = DEFAULT_OUTPUT )] - pub output: Option, + pub output: Option, + /// Sets the tag for the latest version. #[arg( short, @@ -176,13 +203,16 @@ pub struct Opt { value_name = "TAG", allow_hyphen_values = true )] - pub tag: Option, + pub tag: Option, + /// Bumps the version for unreleased changes. #[arg(long, help_heading = Some("FLAGS"))] - pub bump: bool, + pub bump: bool, + /// Prints bumped version for unreleased changes. #[arg(long, help_heading = Some("FLAGS"))] - pub bumped_version: bool, + pub bumped_version: bool, + /// Sets the Tera template to be rendered for each release in the changelog. #[arg( short, @@ -191,42 +221,52 @@ pub struct Opt { value_name = "TEMPLATE", allow_hyphen_values = true )] - pub body_template: Option, + pub body_template: Option, + /// Processes the commits starting from the latest tag. #[arg(short, long, help_heading = Some("FLAGS"))] - pub latest: bool, + pub latest: bool, + /// Processes the commits that belong to the current tag. #[arg(long, help_heading = Some("FLAGS"))] - pub current: bool, + pub current: bool, + /// Processes the commits that do not belong to a tag. #[arg(short, long, help_heading = Some("FLAGS"))] - pub unreleased: bool, + pub unreleased: bool, + /// Sets sorting of releases. #[arg( long, value_enum, default_value_t = TagsOrderBy::Time )] - pub release_order_by: TagsOrderBy, + pub release_order_by: TagsOrderBy, + /// Disables the external command execution. #[arg(long, help_heading = Some("FLAGS"))] - pub no_exec: bool, + pub no_exec: bool, + /// Prints changelog context as JSON. #[arg(short = 'x', long, help_heading = Some("FLAGS"))] - pub context: bool, + pub context: bool, + /// Strips the given parts from the changelog. #[arg(short, long, value_name = "PART", value_enum)] - pub strip: Option, + pub strip: Option, + /// Sets ordering of the commits inside sections. #[arg( long, value_enum, default_value_t = CommitSortOrder::Oldest )] - pub commit_sort_order: CommitSortOrder, + pub commit_sort_order: CommitSortOrder, + /// Sets the commit range to process. #[arg(value_name = "RANGE", help_heading = Some("ARGS"))] - pub range: Option, + pub range: Option, + /// Sets the GitHub API token. #[arg( long, @@ -234,7 +274,8 @@ pub struct Opt { value_name = "TOKEN", hide_env_values = true )] - pub github_token: Option, + pub github_token: Option, + /// Sets the GitHub repository. #[arg( long, @@ -242,7 +283,7 @@ pub struct Opt { value_parser = clap::value_parser!(RemoteValue), value_name = "OWNER/REPO" )] - pub github_repo: Option, + pub github_repo: Option, } /// Custom type for the remote value. diff --git a/git-cliff/src/main.rs b/git-cliff/src/main.rs index 2dd58ba3ab..b25ee4eaa3 100644 --- a/git-cliff/src/main.rs +++ b/git-cliff/src/main.rs @@ -1,12 +1,16 @@ use clap::Parser; use git_cliff::args::Opt; +use git_cliff::args::SubCommands; use git_cliff::logger; use git_cliff_core::error::Result; use std::env; use std::process; fn main() -> Result<()> { - let args = Opt::parse(); + // parse command line arguments + let args: Opt = Opt::parse(); + + // set log level if args.verbose == 1 { env::set_var("RUST_LOG", "debug"); } else if args.verbose > 1 { @@ -15,7 +19,18 @@ fn main() -> Result<()> { env::set_var("RUST_LOG", "info"); } logger::init()?; - match git_cliff::run(args) { + + // run the command or subcommand + let result = match &args.subcommand { + Some(sub_command) => match sub_command { + SubCommands::MigrateConfig(migrate_args) => { + git_cliff_core::config::migrate::run(migrate_args) + } + }, + None => git_cliff::run(args), + }; + + match result { Ok(_) => process::exit(0), Err(e) => { log::error!("{}", e);