cargo_component/commands/
publish.rsuse std::{path::PathBuf, sync::Arc};
use anyhow::{bail, Context, Result};
use cargo_component_core::command::CommonOptions;
use clap::Args;
use wasm_pkg_client::{warg::WargRegistryConfig, Registry};
use crate::{
config::{CargoArguments, CargoPackageSpec, Config},
is_wasm_target, load_metadata, publish, run_cargo_command, PackageComponentMetadata,
PublishOptions,
};
#[derive(Args)]
#[clap(disable_version_flag = true)]
pub struct PublishCommand {
#[clap(flatten)]
pub common: CommonOptions,
#[clap(long = "target", value_name = "TRIPLE")]
pub target: Option<String>,
#[clap(long = "frozen")]
pub frozen: bool,
#[clap(long = "target-dir", value_name = "DIRECTORY")]
pub target_dir: Option<PathBuf>,
#[clap(long = "locked")]
pub locked: bool,
#[clap(long = "package", short = 'p', value_name = "SPEC")]
pub cargo_package: Option<CargoPackageSpec>,
#[clap(long = "manifest-path", value_name = "PATH")]
pub manifest_path: Option<PathBuf>,
#[clap(long = "offline")]
pub offline: bool,
#[clap(long = "features", value_name = "FEATURES")]
pub features: Vec<String>,
#[clap(long = "all-features")]
pub all_features: bool,
#[clap(long = "no-default-features")]
pub no_default_features: bool,
#[clap(long = "jobs", short = 'j', value_name = "N")]
pub jobs: Option<i32>,
#[clap(long = "dry-run")]
pub dry_run: bool,
#[clap(long = "registry", value_name = "REGISTRY")]
pub registry: Option<Registry>,
}
impl PublishCommand {
pub async fn exec(self) -> Result<()> {
log::debug!("executing publish command");
let mut config =
Config::new(self.common.new_terminal(), self.common.config.clone()).await?;
let client = config.client(self.common.cache_dir.clone(), false).await?;
if let Some(target) = &self.target {
if !is_wasm_target(target) {
bail!("target `{}` is not a WebAssembly target", target);
}
}
let metadata = load_metadata(self.manifest_path.as_deref())?;
let spec = match &self.cargo_package {
Some(spec) => Some(spec.clone()),
None => CargoPackageSpec::find_current_package_spec(&metadata),
};
let packages = [PackageComponentMetadata::new(if let Some(spec) = &spec {
metadata
.packages
.iter()
.find(|p| {
p.name == spec.name
&& match spec.version.as_ref() {
Some(v) => &p.version == v,
None => true,
}
})
.with_context(|| {
format!("package ID specification `{spec}` did not match any packages")
})?
} else {
metadata
.root_package()
.context("no root package found in manifest")?
})?];
let package = packages[0].package;
let component_metadata = &packages[0].metadata;
let name = component_metadata.section.package.as_ref().with_context(|| {
format!(
"package `{name}` is missing a `package.metadata.component.package` setting in manifest `{path}`",
name = package.name,
path = package.manifest_path
)
})?;
if let Ok(key) = std::env::var("CARGO_COMPONENT_PUBLISH_KEY") {
let registry = config.pkg_config.resolve_registry(name).ok_or_else(|| anyhow::anyhow!("Tried to set a signing key, but registry was not set and no default registry was found. Try setting the `--registry` option."))?.to_owned();
let reg_config = config
.pkg_config
.get_or_insert_registry_config_mut(®istry);
let mut warg_conf = WargRegistryConfig::try_from(&*reg_config).unwrap_or_default();
warg_conf.signing_key = Some(Arc::new(
key.try_into().context("Failed to parse signing key")?,
));
reg_config.set_backend_config("warg", warg_conf)?;
}
let cargo_build_args = CargoArguments {
color: self.common.color,
verbose: self.common.verbose as usize,
help: false,
quiet: self.common.quiet,
targets: self.target.clone().into_iter().collect(),
manifest_path: self.manifest_path.clone(),
message_format: None,
frozen: self.frozen,
locked: self.locked,
release: true,
offline: self.offline,
workspace: false,
packages: self.cargo_package.clone().into_iter().collect(),
};
let spawn_args = self.build_args()?;
let outputs = run_cargo_command(
client.clone(),
&config,
&metadata,
&packages,
Some("build"),
&cargo_build_args,
&spawn_args,
)
.await?;
if outputs.len() != 1 {
bail!(
"expected one output from `cargo build`, got {len}",
len = outputs.len()
);
}
let options = PublishOptions {
package,
name,
registry: self.registry.as_ref(),
version: &component_metadata.version,
path: &outputs[0],
dry_run: self.dry_run,
};
publish(&config, client, &options).await
}
fn build_args(&self) -> Result<Vec<String>> {
let mut args = Vec::new();
args.push("build".to_string());
args.push("--release".to_string());
if self.common.quiet {
args.push("-q".to_string());
}
args.extend(
std::iter::repeat("-v")
.take(self.common.verbose as usize)
.map(ToString::to_string),
);
if let Some(color) = self.common.color {
args.push("--color".to_string());
args.push(color.to_string());
}
if let Some(target) = &self.target {
args.push("--target".to_string());
args.push(target.clone());
}
if self.frozen {
args.push("--frozen".to_string());
}
if let Some(target_dir) = &self.target_dir {
args.push("--target-dir".to_string());
args.push(
target_dir
.as_os_str()
.to_str()
.with_context(|| {
format!(
"target directory `{dir}` is not valid UTF-8",
dir = target_dir.display()
)
})?
.to_string(),
);
}
if self.locked {
args.push("--locked".to_string());
}
if let Some(spec) = &self.cargo_package {
args.push("--package".to_string());
args.push(spec.to_string());
}
if let Some(manifest_path) = &self.manifest_path {
args.push("--manifest-path".to_string());
args.push(
manifest_path
.as_os_str()
.to_str()
.with_context(|| {
format!(
"manifest path `{path}` is not valid UTF-8",
path = manifest_path.display()
)
})?
.to_string(),
);
}
if self.offline {
args.push("--offline".to_string());
}
if !self.features.is_empty() {
args.push("--features".to_string());
args.push(self.features.join(","));
}
if self.all_features {
args.push("--all-features".to_string());
}
if self.no_default_features {
args.push("--no-default-features".to_string());
}
if let Some(jobs) = self.jobs {
args.push("--jobs".to_string());
args.push(jobs.to_string());
}
Ok(args)
}
}