Skip to content

Commit

Permalink
Merge pull request #1299 from Barsik-sus/willbe_show_plan_before_exec…
Browse files Browse the repository at this point in the history
…ution

READY: (willbe): Refactor publish action to be able to display the publication plan before execution
  • Loading branch information
Wandalen authored May 1, 2024
2 parents 39c2ffc + b989df5 commit 41bd926
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 44 deletions.
124 changes: 86 additions & 38 deletions module/move/willbe/src/action/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ mod private
{
/// Represents the absolute path to the root directory of the workspace.
pub workspace_root_dir : Option< AbsolutePath >,
/// Represents a collection of packages that are roots of the trees.
pub wanted_to_publish : Vec< CrateDir >,
pub plan : Option< package::PublishPlan >,
/// Represents a collection of packages and their associated publishing reports.
pub packages : Vec<( AbsolutePath, package::PublishReport )>
Expand All @@ -34,16 +32,8 @@ mod private
write!( f, "Nothing to publish" )?;
return Ok( () );
}
if let Some( plan ) = &self.plan
{
write!( f, "Tree{} :\n", if self.wanted_to_publish.len() > 1 { "s" } else { "" } )?;
plan.display_as_tree( f, &self.wanted_to_publish )?;

writeln!( f, "The following packages are pending for publication :" )?;
plan.display_as_list( f )?;
}

writeln!( f, "\nActions :" )?;
writeln!( f, "Actions :" )?;
for ( path, report ) in &self.packages
{
let report = report.to_string().replace("\n", "\n ");
Expand All @@ -58,78 +48,117 @@ mod private
};
write!( f, "Publishing crate by `{}` path\n {report}", path.display() )?;
}
if let Some( plan ) = &self.plan
{
if !plan.dry
{
let expected_to_publish = plan
.plans
.iter()
.map( | p | ( p.version_bump.crate_dir.absolute_path(), p.package_name.clone(), p.version_bump.clone() ) )
.collect::< Vec< _ > >();
let mut actually_published = self.packages.iter()
.filter_map
(
|( path, repo )|
if repo.publish.as_ref().is_some_and( | r | r.error.is_ok() )
{
Some( path.clone() )
}
else
{
None
}
)
.collect::< Vec< _ > >();

writeln!( f, "Status :" )?;
for ( path, name, version ) in expected_to_publish
{
if let Some( pos ) = actually_published.iter().position( | p | p == &path )
{
write!( f, "✅ {name} {}", version.new_version )?;
// want to check that only expected packages actually published
_ = actually_published.remove( pos );
}
else
{
write!( f, "❌ {name} {}", version.old_version )?;
}
}
if !actually_published.is_empty()
{
writeln!( f, "Logical error. Published unexpected packages" )?;
return Err( std::fmt::Error );
}
}
}

Ok( () )
}
}

/// Publishes packages based on the specified patterns.
///
/// Publish packages.
/// # Arguments
/// * `patterns` - A vector of patterns specifying the folders to search for packages.
/// * `dry` - A boolean value indicating whether to perform a dry run.
/// * `temp` - A boolean value indicating whether to use a temporary directory.
///

/// # Returns
/// A Result containing a `PublishPlan` if successful, or an `Error` otherwise.
#[ cfg_attr( feature = "tracing", tracing::instrument ) ]
pub fn publish( patterns : Vec< String >, dry : bool, temp : bool ) -> Result< PublishReport, ( PublishReport, Error ) >
pub fn publish_plan( patterns : Vec< String >, dry : bool, temp : bool ) -> Result< package::PublishPlan, Error >
{
let mut report = PublishReport::default();

let mut paths = HashSet::new();
// find all packages by specified folders
for pattern in &patterns
{
let current_path = AbsolutePath::try_from( std::path::PathBuf::from( pattern ) ).err_with( || report.clone() )?;
let current_path = AbsolutePath::try_from( std::path::PathBuf::from( pattern ) )?;
// let current_paths = files::find( current_path, &[ "Cargo.toml" ] );
paths.extend( Some( current_path ) );
}

let mut metadata = if paths.is_empty()
{
Workspace::from_current_path().err_with( || report.clone() )?
Workspace::from_current_path()?
}
else
{
// FIX : patterns can point to different workspaces. Current solution take first random path from list
let current_path = paths.iter().next().unwrap().clone();
let dir = CrateDir::try_from( current_path ).err_with( || report.clone() )?;
let dir = CrateDir::try_from( current_path )?;

Workspace::with_crate_dir( dir ).err_with( || report.clone() )?
Workspace::with_crate_dir( dir )?
};
let workspace_root_dir : AbsolutePath = metadata
.workspace_root()
.err_with( || report.clone() )?
.try_into()
.err_with( || report.clone() )?;
report.workspace_root_dir = Some( workspace_root_dir.clone() );
let packages = metadata.load().err_with( || report.clone() )?.packages().err_with( || report.clone() )?;
.workspace_root()?
.try_into()?;
let packages = metadata.load()?.packages()?;
let packages_to_publish : Vec< _ > = packages
.iter()
.filter( | &package | paths.contains( &AbsolutePath::try_from( package.manifest_path().as_std_path().parent().unwrap() ).unwrap() ) )
.map( | p | p.name().clone() )
.collect();
let package_map = packages.into_iter().map( | p | ( p.name().clone(), Package::from( p.clone() ) ) ).collect::< HashMap< _, _ > >();
{
for node in &packages_to_publish
{
report.wanted_to_publish.push( package_map.get( node ).unwrap().crate_dir() );
}
}

let graph = metadata.graph();
let subgraph_wanted = graph::subgraph( &graph, &packages_to_publish );
let tmp = subgraph_wanted.map( | _, n | graph[ *n ].clone(), | _, e | graph[ *e ].clone() );

let mut unique_name = format!( "temp_dir_for_publish_command_{}", path_tools::path::unique_folder_name().err_with( || report.clone() )? );
let mut unique_name = format!( "temp_dir_for_publish_command_{}", path_tools::path::unique_folder_name()? );

let dir = if temp
{
let mut temp_dir = env::temp_dir().join( unique_name );

while temp_dir.exists()
{
unique_name = format!( "temp_dir_for_publish_command_{}", path_tools::path::unique_folder_name().err_with( || report.clone() )? );
unique_name = format!( "temp_dir_for_publish_command_{}", path_tools::path::unique_folder_name()? );
temp_dir = env::temp_dir().join( unique_name );
}

fs::create_dir( &temp_dir ).err_with( || report.clone() )?;
fs::create_dir( &temp_dir )?;
Some( temp_dir )
}
else
Expand All @@ -142,22 +171,39 @@ mod private

let queue = graph::toposort( subgraph ).unwrap().into_iter().map( | n | package_map.get( &n ).unwrap() ).cloned().collect::< Vec< _ > >();

let roots = packages_to_publish.iter().map( | p | package_map.get( p ).unwrap().crate_dir() ).collect::< Vec< _ > >();

let plan = package::PublishPlan::former()
.workspace_dir( CrateDir::try_from( workspace_root_dir ).unwrap() )
.option_base_temp_dir( dir.clone() )
.dry( dry )
.roots( roots )
.packages( queue )
.form();

Ok( plan )
}

///
/// Publish packages.
///

#[ cfg_attr( feature = "tracing", tracing::instrument ) ]
pub fn publish( plan : package::PublishPlan ) -> Result< PublishReport, ( PublishReport, Error ) >
{
let mut report = PublishReport::default();
let temp = plan.base_temp_dir.clone();

report.plan = Some( plan.clone() );
for package_report in package::perform_packages_publish( plan ).err_with( || report.clone() )?
{
let path : &std::path::Path = package_report.get_info.as_ref().unwrap().current_path.as_ref();
report.packages.push(( AbsolutePath::try_from( path ).unwrap(), package_report ));
}

if temp
if let Some( dir ) = temp
{
fs::remove_dir_all( dir.unwrap() ).err_with( || report.clone() )?;
fs::remove_dir_all( dir ).err_with( || report.clone() )?;
}

Ok( report )
Expand Down Expand Up @@ -188,6 +234,8 @@ mod private

crate::mod_interface!
{
/// Publish package.
/// Create a plan for publishing packages
orphan use publish_plan;
/// Execute the publication plan
orphan use publish;
}
17 changes: 15 additions & 2 deletions module/move/willbe/src/command/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ mod private
use colored::Colorize;

use wca::VerifiedCommand;
use wtools::error::Result;
use wtools::error::{ Result, for_app::Context };
use former::Former;
use std::fmt::Write;

#[ derive( Former ) ]
struct PublishProperties
Expand All @@ -29,8 +30,20 @@ mod private
let patterns : Vec< _ > = o.args.get_owned( 0 ).unwrap_or_else( || vec![ "./".into() ] );

let PublishProperties { dry, temp } = o.props.try_into()?;
let plan = action::publish_plan( patterns, dry, temp ).context( "Failed to plan the publication process" )?;

match action::publish( patterns, dry, temp )
let mut formatted_plan = String::new();
writeln!( &mut formatted_plan, "Tree :" )?;
plan.write_as_tree( &mut formatted_plan )?;

if !plan.plans.is_empty()
{
writeln!( &mut formatted_plan, "The following packages are pending for publication :" )?;
plan.write_as_list( &mut formatted_plan )?;
}
println!( "{formatted_plan}" );

match action::publish( plan )
{
Ok( report ) =>
{
Expand Down
14 changes: 10 additions & 4 deletions module/move/willbe/src/entity/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,9 @@ mod private
#[ default( true ) ]
pub dry : bool,

/// Required for tree view only
pub roots : Vec< CrateDir >,

/// `plans` - This is a vector containing the instructions for publishing each package. Each item
/// in the `plans` vector indicates a `PackagePublishInstruction` set for a single package. It outlines
/// how to build and where to publish the package amongst other instructions. The `#[setter( false )]`
Expand All @@ -500,19 +503,20 @@ mod private
/// # Arguments
///
/// * `f` - A mutable reference to a `Formatter` used for writing the output.
/// * `roots` - A slice of `CrateDir` representing the root crates to display.
///
/// # Errors
///
/// Returns a `std::fmt::Error` if there is an error writing to the formatter.
pub fn display_as_tree( &self, f : &mut Formatter< '_ >, roots : &[ CrateDir ] ) -> std::fmt::Result
pub fn write_as_tree< W >( &self, f : &mut W ) -> std::fmt::Result
where
W : std::fmt::Write
{
let name_bump_report = self
.plans
.iter()
.map( | x | ( &x.package_name, ( x.version_bump.old_version.to_string(), x.version_bump.new_version.to_string() ) ) )
.collect::< HashMap< _, _ > >();
for wanted in roots
for wanted in &self.roots
{
let list = action::list
(
Expand Down Expand Up @@ -556,7 +560,9 @@ mod private
/// # Errors
///
/// Returns a `std::fmt::Error` if there is an error writing to the formatter.
pub fn display_as_list( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result
pub fn write_as_list< W >( &self, f : &mut W ) -> std::fmt::Result
where
W : std::fmt::Write
{
for ( idx, package ) in self.plans.iter().enumerate()
{
Expand Down

0 comments on commit 41bd926

Please sign in to comment.