diff --git a/module/move/willbe/src/action/publish.rs b/module/move/willbe/src/action/publish.rs index 58412a2d7a..048220bc20 100644 --- a/module/move/willbe/src/action/publish.rs +++ b/module/move/willbe/src/action/publish.rs @@ -109,6 +109,7 @@ mod private /// /// # Arguments /// * `patterns` - A vector of patterns specifying the folders to search for packages. + /// * `exclude_dev_dependencies` - A boolean value indicating whether to exclude dev dependencies from manifest before publish. /// * `dry` - A boolean value indicating whether to perform a dry run. /// * `temp` - A boolean value indicating whether to use a temporary directory. /// @@ -119,6 +120,8 @@ mod private ( patterns : Vec< String >, channel : channel::Channel, + exclude_dev_dependencies : bool, + commit_changes : bool, dry : bool, temp : bool ) @@ -233,6 +236,8 @@ mod private .channel( channel ) .workspace_dir( CrateDir::try_from( workspace_root_dir ).unwrap() ) .option_base_temp_dir( dir.clone() ) + .exclude_dev_dependencies( exclude_dev_dependencies ) + .commit_changes( commit_changes ) .dry( dry ) .roots( roots ) .packages( queue ) diff --git a/module/move/willbe/src/action/publish_diff.rs b/module/move/willbe/src/action/publish_diff.rs index 11b1758ca8..500a73a7f8 100644 --- a/module/move/willbe/src/action/publish_diff.rs +++ b/module/move/willbe/src/action/publish_diff.rs @@ -22,6 +22,7 @@ mod private pub struct PublishDiffOptions { path : PathBuf, + exclude_dev_dependencies : bool, keep_archive : Option< PathBuf >, } @@ -141,16 +142,17 @@ mod private let name = &package.name()?; let version = &package.version()?; - _ = cargo::pack - ( - cargo::PackOptions::former() - .path( dir.as_ref() ) - .allow_dirty( true ) - .checking_consistency( false ) - .dry( false ).form() - )?; - let l = CrateArchive::read( packed_crate::local_path( name, version, dir )? )?; - let r = CrateArchive::download_crates_io( name, version ).unwrap(); + _ = cargo::pack + ( + cargo::PackOptions::former() + .path( dir.as_ref() ) + .allow_dirty( true ) + .checking_consistency( false ) + .exclude_dev_dependencies( o.exclude_dev_dependencies) + .dry( false ).form() + )?; + let l = CrateArchive::read( packed_crate::local_path( name, version, dir )? )?; + let r = CrateArchive::download_crates_io( name, version ).unwrap(); if let Some( out_path ) = &o.keep_archive diff --git a/module/move/willbe/src/command/mod.rs b/module/move/willbe/src/command/mod.rs index bae53834e1..5c81dcf47b 100644 --- a/module/move/willbe/src/command/mod.rs +++ b/module/move/willbe/src/command/mod.rs @@ -25,6 +25,16 @@ mod private .kind( Type::String ) .optional( true ) .end() + .property( "exclude_dev_dependencies" ) + .hint( "Setting this option to true will temporarily remove development dependencies before executing the command, then restore them afterward. Default is `true`." ) + .kind( Type::Bool ) + .optional( true ) + .end() + .property( "commit_changes" ) + .hint( "Indicates whether changes should be committed. Default is `false`." ) + .kind( Type::Bool ) + .optional( true ) + .end() .property( "dry" ) .hint( "Enables 'dry run'. Does not publish, only simulates. Default is `true`." ) .kind( Type::Bool ) @@ -47,6 +57,11 @@ mod private .kind( Type::Path ) .optional( true ) .end() + .property( "exclude_dev_dependencies" ) + .hint( "Setting this option to true will temporarily remove development dependencies before executing the command, then restore them afterward. Default is `true`." ) + .kind( Type::Bool ) + .optional( true ) + .end() .property( "keep_archive" ) .hint( "Save remote package version to the specified path" ) .kind( Type::Path ) diff --git a/module/move/willbe/src/command/publish.rs b/module/move/willbe/src/command/publish.rs index a70af4265d..b9ca441ef5 100644 --- a/module/move/willbe/src/command/publish.rs +++ b/module/move/willbe/src/command/publish.rs @@ -15,6 +15,10 @@ mod private { #[ former( default = Channel::Stable ) ] channel : Channel, + #[ former( default = false ) ] + exclude_dev_dependencies : bool, + #[ former( default = false ) ] + commit_changes : bool, #[ former( default = true ) ] dry : bool, #[ former( default = true ) ] @@ -52,10 +56,12 @@ mod private let PublishProperties { channel, + exclude_dev_dependencies, + commit_changes, dry, temp } = o.props.try_into()?; - let plan = action::publish_plan( patterns, channel, dry, temp ) + let plan = action::publish_plan( patterns, channel, exclude_dev_dependencies, commit_changes, dry, temp ) .context( "Failed to plan the publication process" )?; let mut formatted_plan = String::new(); @@ -110,6 +116,10 @@ mod private else { this }; + this = if let Some( v ) = value + .get_owned( "exclude_dev_dependencies" ) { this.exclude_dev_dependencies::< bool >( v ) } else { this }; + this = if let Some( v ) = value + .get_owned( "commit_changes" ) { this.commit_changes::< bool >( v ) } else { this }; this = if let Some( v ) = value .get_owned( "dry" ) { this.dry::< bool >( v ) } else { this }; this = if let Some( v ) = value diff --git a/module/move/willbe/src/command/publish_diff.rs b/module/move/willbe/src/command/publish_diff.rs index 4691331866..74cdbcbc48 100644 --- a/module/move/willbe/src/command/publish_diff.rs +++ b/module/move/willbe/src/command/publish_diff.rs @@ -13,6 +13,8 @@ mod private #[ derive( former::Former ) ] struct PublishDiffProperties { + #[ former( default = false ) ] + exclude_dev_dependencies : bool, keep_archive : Option< PathBuf >, } @@ -33,10 +35,11 @@ mod private pub fn publish_diff( o : VerifiedCommand ) -> error::untyped::Result< () > // qqq : use typed error { let path : PathBuf = o.args.get_owned( 0 ).unwrap_or( std::env::current_dir()? ); - let PublishDiffProperties { keep_archive } = o.props.try_into()?; + let PublishDiffProperties { keep_archive, exclude_dev_dependencies } = o.props.try_into()?; let mut o = action::PublishDiffOptions::former() - .path( path ); + .path( path ) + .exclude_dev_dependencies( exclude_dev_dependencies ); if let Some( k ) = keep_archive.clone() { o = o.keep_archive( k ); } let o = o.form(); @@ -57,6 +60,12 @@ mod private { let mut this = Self::former(); + this = if let Some( v ) = value + .get_owned( "exclude_dev_dependencies" ) + { this.exclude_dev_dependencies::< bool >( v ) } + else + { this }; + this = if let Some( v ) = value .get_owned( "keep_archive" ) { this.keep_archive::< PathBuf >( v ) } diff --git a/module/move/willbe/src/entity/publish.rs b/module/move/willbe/src/entity/publish.rs index 497ee6c90f..f7cc9965c8 100644 --- a/module/move/willbe/src/entity/publish.rs +++ b/module/move/willbe/src/entity/publish.rs @@ -26,7 +26,7 @@ mod private /// Options for bumping the package version. pub bump : version::BumpOptions, /// Git options related to the package. - pub git_options : entity::git::GitOptions, + pub git_options : Option< entity::git::GitOptions >, /// Options for publishing the package using Cargo. pub publish : cargo::PublishOptions, /// Indicates whether the process should be dry-run (no actual publishing). @@ -42,6 +42,9 @@ mod private package : package::Package< 'a >, channel : channel::Channel, base_temp_dir : Option< path::PathBuf >, + exclude_dev_dependencies : bool, + #[ former( default = true ) ] + commit_changes : bool, #[ former( default = true ) ] dry : bool, } @@ -58,6 +61,7 @@ mod private channel : self.channel, allow_dirty : self.dry, checking_consistency : !self.dry, + exclude_dev_dependencies : self.exclude_dev_dependencies, temp_path : self.base_temp_dir.clone(), dry : self.dry, }; @@ -73,17 +77,21 @@ mod private dependencies : dependencies.clone(), dry : self.dry, }; - let git_options = entity::git::GitOptions + let git_options = if self.commit_changes { - git_root : workspace_root, - items : dependencies.iter().chain([ &crate_dir ]).map( | d | d.clone().absolute_path().join( "Cargo.toml" ) ).collect(), - message : format!( "{}-v{}", self.package.name().unwrap(), new_version ), - dry : self.dry, - }; + Some( entity::git::GitOptions + { + git_root : workspace_root, + items : dependencies.iter().chain([ &crate_dir ]).map( | d | d.clone().absolute_path().join( "Cargo.toml" ) ).collect(), + message : format!( "{}-v{}", self.package.name().unwrap(), new_version ), + dry : self.dry, + }) + } else { None }; let publish = cargo::PublishOptions { path : crate_dir.clone().absolute_path().inner(), temp_path : self.base_temp_dir.clone(), + exclude_dev_dependencies : self.exclude_dev_dependencies, retry_count : 2, dry : self.dry, }; @@ -121,6 +129,14 @@ mod private /// Release channels for rust. pub channel : channel::Channel, + /// Setting this option to true will temporarily remove development dependencies before executing the command, then restore them afterward. + #[ allow( dead_code ) ] // former related + pub exclude_dev_dependencies : bool, + + /// Indicates whether changes should be committed. + #[ former( default = true ) ] + pub commit_changes : bool, + /// `dry` - A boolean value indicating whether to do a dry run. If set to `true`, the application performs /// a simulated run without making any actual changes. If set to `false`, the operations are actually executed. /// This property is optional and defaults to `true`. @@ -246,6 +262,14 @@ mod private { plan = plan.dry( dry ); } + if let Some( exclude_dev_dependencies ) = &self.storage.exclude_dev_dependencies + { + plan = plan.exclude_dev_dependencies( *exclude_dev_dependencies ) + } + if let Some( commit_changes ) = &self.storage.commit_changes + { + plan = plan.commit_changes( *commit_changes ) + } let plan = plan .channel( channel ) .package( package ) @@ -362,45 +386,55 @@ mod private } = instruction; pack.dry = dry; bump.dry = dry; - git_options.dry = dry; + git_options.as_mut().map( | d | d.dry = dry ); publish.dry = dry; report.get_info = Some( cargo::pack( pack ).err_with_report( &report )? ); // aaa : redundant field? // aaa : removed let bump_report = version::bump( bump ).err_with_report( &report )?; report.bump = Some( bump_report.clone() ); - let git_root = git_options.git_root.clone(); - let git = match entity::git::perform_git_commit( git_options ) + + let git_root = git_options.as_ref().map( | g | g.git_root.clone() ); + if let Some( git_options ) = git_options { - Ok( git ) => git, - Err( e ) => + let git = match entity::git::perform_git_commit( git_options ) { - version::revert( &bump_report ) - .map_err( | le | format_err!( "Base error:\n{}\nRevert error:\n{}", e.to_string().replace( '\n', "\n\t" ), le.to_string().replace( '\n', "\n\t" ) ) ) - .err_with_report( &report )?; - return Err(( report, e )); - } - }; - report.add = git.add; - report.commit = git.commit; + Ok( git ) => git, + Err( e ) => + { + version::revert( &bump_report ) + .map_err( | le | format_err!( "Base error:\n{}\nRevert error:\n{}", e.to_string().replace( '\n', "\n\t" ), le.to_string().replace( '\n', "\n\t" ) ) ) + .err_with_report( &report )?; + return Err(( report, e )); + } + }; + report.add = git.add; + report.commit = git.commit; + } report.publish = match cargo::publish( publish ) { Ok( publish ) => Some( publish ), Err( e ) => { - tool::git::reset( git_root.as_ref(), true, 1, false ) - .map_err - ( - | le | - format_err!( "Base error:\n{}\nRevert error:\n{}", e.to_string().replace( '\n', "\n\t" ), le.to_string().replace( '\n', "\n\t" ) ) - ) - .err_with_report( &report )?; + if let Some( git_root ) = git_root.as_ref() + { + tool::git::reset( git_root.as_ref(), true, 1, false ) + .map_err + ( + | le | + format_err!( "Base error:\n{}\nRevert error:\n{}", e.to_string().replace( '\n', "\n\t" ), le.to_string().replace( '\n', "\n\t" ) ) + ) + .err_with_report( &report )?; + } return Err(( report, e )); } }; - let res = tool::git::push( &git_root, dry ).err_with_report( &report )?; - report.push = Some( res ); + if let Some( git_root ) = git_root.as_ref() + { + let res = tool::git::push( &git_root, dry ).err_with_report( &report )?; + report.push = Some( res ); + } Ok( report ) } diff --git a/module/move/willbe/src/tool/cargo.rs b/module/move/willbe/src/tool/cargo.rs index 4e0bd9a330..780defb921 100644 --- a/module/move/willbe/src/tool/cargo.rs +++ b/module/move/willbe/src/tool/cargo.rs @@ -1,6 +1,8 @@ /// Internal namespace. mod private { + use crate::*; + #[ allow( unused_imports ) ] use crate::tool::*; @@ -47,6 +49,9 @@ mod private // aaa : don't abuse negative form, rename to checking_consistency // renamed and changed logic pub( crate ) checking_consistency : bool, + /// Setting this option to true will temporarily remove development dependencies before executing the command, then restore them afterward. + #[ former( default = true ) ] + pub( crate ) exclude_dev_dependencies : bool, /// An optional temporary path to be used during packaging. /// /// This field may contain a path to a temporary directory that will be used during the packaging process. @@ -79,6 +84,53 @@ mod private } } + #[ derive( Debug ) ] + struct TemporaryManifestFile + { + original : PathBuf, + temporary : PathBuf, + } + + impl TemporaryManifestFile + { + /// Creates a backup copy of the original file, allowing the original file location to serve as a temporary workspace. + /// When the object is dropped, the temporary file at the original location is replaced by the backup, restoring the original file. + fn new( path : impl Into< PathBuf > ) -> error::untyped::Result< Self > + { + let path = path.into(); + if !path.ends_with( "Cargo.toml" ) + { + error::untyped::bail!( "Wrong path to temporary manifest" ); + } + + let mut index = 0; + let original = loop + { + let temp_path = PathBuf::from( format!( "{}.temp_{index}", path.display() ) ); + if !temp_path.exists() + { + _ = std::fs::copy( &path, &temp_path )?; + break temp_path; + } + index += 1; + }; + + Ok( Self + { + original, + temporary : path, + }) + } + } + + impl Drop for TemporaryManifestFile + { + fn drop( &mut self ) + { + _ = std::fs::rename( &self.original, &self.temporary ).ok(); + } + } + /// /// Assemble the local package into a distributable tarball. /// @@ -96,6 +148,17 @@ mod private // qqq : use typed error pub fn pack( args : PackOptions ) -> error::untyped::Result< process::Report > { + let _temp = if args.exclude_dev_dependencies + { + let manifest = TemporaryManifestFile::new( args.path.join( "Cargo.toml" ) )?; + let mut file = Manifest::try_from( ManifestFile::try_from( &manifest.temporary )? )?; + let data = file.data(); + + _ = data.remove( "dev-dependencies" ); + file.store()?; + + Some( manifest ) + } else { None }; let ( program, options ) = ( "rustup", args.to_pack_args() ); if args.dry @@ -129,6 +192,7 @@ mod private { pub( crate ) path : PathBuf, pub( crate ) temp_path : Option< PathBuf >, + pub( crate ) exclude_dev_dependencies : bool, #[ former( default = 0usize ) ] pub( crate ) retry_count : usize, pub( crate ) dry : bool, @@ -162,6 +226,17 @@ mod private pub fn publish( args : PublishOptions ) -> error::untyped::Result< process::Report > // qqq : use typed error { + let _temp = if args.exclude_dev_dependencies + { + let manifest = TemporaryManifestFile::new( args.path.join( "Cargo.toml" ) )?; + let mut file = Manifest::try_from( ManifestFile::try_from( &manifest.temporary )? )?; + let data = file.data(); + + _ = data.remove( "dev-dependencies" ); + file.store()?; + + Some( manifest ) + } else { None }; let ( program, arguments) = ( "cargo", args.as_publish_args() ); if args.dry