use crate::{graph, version};
use anyhow::{anyhow, Result};
use ccli::clap::{self, Parser};
use core::str::FromStr;
use std::{path::PathBuf, process::Command};
use toml_edit::Document;
#[derive(Debug, Parser, Clone)]
pub struct Publish {
#[clap(short, long)]
dry_run: bool,
}
impl Publish {
pub fn run(&self, manifest: &PathBuf, ignore: &[String]) -> Result<()> {
let workspace = Document::from_str(&std::fs::read_to_string(manifest)?)?;
let version = workspace["workspace"]["package"]["version"]
.as_str()
.ok_or_else(|| anyhow!("Failed to parse version from workspace {manifest:?}"))?;
let order = graph::resolve(manifest, ignore)?;
let mut published = 0u32;
let mut skipped = 0u32;
let mut failed = Vec::new();
for pkg in order {
if version::verify(&pkg, version)? {
println!("{pkg}@{version} already published, skipping");
skipped += 1;
continue;
}
if self.dry_run {
println!("{pkg}@{version} would publish");
continue;
}
if let Err(err) = self.publish(&pkg) {
eprintln!("failed to publish {pkg}: {err}, continuing");
failed.push(pkg);
} else {
published += 1;
}
}
println!(
"\nsummary: {published} published, {skipped} skipped, {} failed",
failed.len(),
);
for pkg in &failed {
println!(" failed: {pkg}");
}
if !failed.is_empty() {
return Err(anyhow!("{} crate(s) failed to publish", failed.len()));
}
Ok(())
}
fn publish(&self, package: &str) -> Result<()> {
let status = Command::new("cargo")
.arg("publish")
.arg("-p")
.arg(package)
.arg("--allow-dirty")
.status()?;
if !status.success() {
return Err(anyhow!("cargo publish -p {package} exited with {status}"));
}
Ok(())
}
}