monochange 0.5.1

Manage versions and releases for your multiplatform, multilanguage monorepo
Documentation
use std::io;
use std::io::IsTerminal;

use monochange_publish::EcosystemProgressPresentation;
use monochange_publish::PublishProgressEvent;
use monochange_publish::PublishProgressPackage;
use monochange_publish::PublishProgressReporter;

pub(crate) struct StderrPublishProgressReporter {
	enabled: bool,
	interactive: bool,
}

impl StderrPublishProgressReporter {
	pub(crate) fn new(quiet: bool) -> Self {
		let no_progress = std::env::var_os("MONOCHANGE_NO_PROGRESS").is_some();
		let interactive = io::stderr().is_terminal() && std::env::var_os("CI").is_none();
		Self {
			enabled: !quiet && !no_progress,
			interactive,
		}
	}

	pub(crate) fn render_event(event: &PublishProgressEvent, interactive: bool) -> String {
		match event {
			PublishProgressEvent::RunStarted {
				mode,
				dry_run,
				total,
				ecosystems,
			} => {
				let ecosystems = ecosystems
					.iter()
					.map(|ecosystem| {
						format!(
							"{} {}",
							ecosystem.progress_emoji(),
							ecosystem.progress_label()
						)
					})
					.collect::<Vec<_>>()
					.join(", ");
				let dry_run = if *dry_run { " dry-run" } else { "" };
				format!("◆ Publishing {total} packages ({mode:?}{dry_run}) across {ecosystems}")
			}
			PublishProgressEvent::RegistryCheckStarted(package) => {
				format!(
					"{} {} checking {} on {}",
					start_symbol(interactive),
					package_prefix(package),
					package.version,
					package.registry
				)
			}
			PublishProgressEvent::PackageStarted(package) => {
				format!(
					"{} {} publishing {} to {}",
					start_symbol(interactive),
					package_prefix(package),
					package.version,
					package.registry
				)
			}
			PublishProgressEvent::PackageSkipped { package, message } => {
				format!("⏭️ {} {message}", package_prefix(package))
			}
			PublishProgressEvent::PackagePlanned(package) => {
				format!(
					"📝 {} would publish {} to {}",
					package_prefix(package),
					package.version,
					package.registry
				)
			}
			PublishProgressEvent::PackagePublished(package) => {
				format!(
					"{} published {} to {}",
					package_prefix(package),
					package.version,
					package.registry
				)
			}
			PublishProgressEvent::PackageFailed { package, message } => {
				format!("{} failed: {message}", package_prefix(package))
			}
			PublishProgressEvent::RunFinished {
				total,
				published,
				skipped,
				failed,
				..
			} => {
				format!(
					"◆ Publish complete: {total} packages, ✅ {published} published, ⏭️ {skipped} skipped, ❌ {failed} failed"
				)
			}
		}
	}
}

impl PublishProgressReporter for StderrPublishProgressReporter {
	fn report(&self, event: PublishProgressEvent) {
		if !self.enabled {
			return;
		}
		eprintln!("{}", Self::render_event(&event, self.interactive));
	}
}

fn start_symbol(interactive: bool) -> &'static str {
	if interactive { "" } else { "" }
}

fn package_prefix(package: &PublishProgressPackage) -> String {
	format!(
		"{} {} {}",
		package.ecosystem.progress_emoji(),
		package.ecosystem.progress_label(),
		package.package_name
	)
}

#[cfg(test)]
#[path = "__tests__/publish_progress_tests.rs"]
mod tests;