use std::path::{Path, PathBuf};
use anyhow::{bail, Context};
use is_terminal::IsTerminal;
use wasmer_api::backend::gql::PublishDeployAppVariables;
use wasmer_deploy_schema::schema::DeploymentV1;
use crate::{cmd::AsyncCliCommand, ApiOpts, ItemFormatOpts};
#[derive(clap::Parser, Debug)]
pub struct CmdAppPublish {
#[clap(flatten)]
pub api: ApiOpts,
#[clap(flatten)]
pub fmt: ItemFormatOpts,
#[clap(long)]
pub no_validate: bool,
#[clap(long)]
pub publish_package: bool,
#[clap(long)]
pub non_interactive: bool,
pub path: Option<PathBuf>,
}
impl CmdAppPublish {
async fn run(self) -> Result<(), anyhow::Error> {
let base_path = if let Some(p) = self.path {
p
} else {
std::env::current_dir()?
};
let file_path = if base_path.is_file() {
base_path
} else if base_path.is_dir() {
let full = base_path.join("deploy.yaml");
if !full.is_file() {
bail!("Could not find deploy.yaml at path: '{}'", full.display());
}
full
} else {
bail!("No such file or directory: '{}'", base_path.display());
};
let dir_path = file_path.canonicalize()?.parent().unwrap().to_owned();
let interactive = std::io::stdin().is_terminal() && !self.non_interactive;
let client = self.api.client()?;
let user = wasmer_api::backend::current_user(&client).await?;
let contents = std::fs::read_to_string(&file_path)
.with_context(|| format!("Could not read file: '{}'", file_path.display()))?;
let config =
serde_yaml::from_str::<DeploymentV1>(&contents).context("Could not parse schema")?;
let (owner, name) = if let Some((ns, name)) = config.name.split_once('/') {
(ns.to_string(), name.to_string())
} else {
(user.username, config.name.clone())
};
if let Some(manifest) = Self::load_wasmer_manifest(&dir_path)? {
if let Some(webc) = config.workload.webc() {
let full_name_webc = format!("{}/{}", webc.namespace, webc.name);
if full_name_webc == manifest.package.name {
let should_publish = if self.publish_package {
true
} else if interactive {
eprintln!("Found local package in wasmer.toml");
dialoguer::Confirm::new()
.with_prompt(format!("Publish package '{}'?", full_name_webc))
.interact_opt()?
.unwrap_or_default()
} else {
false
};
if should_publish {
eprintln!("Publishing package...");
let new_manifest = crate::util::republish_package_with_bumped_version(
&client,
&dir_path.join("wasmer.toml"),
manifest,
)
.await?;
eprintln!(
"Package '{}@{}' published successfully!",
new_manifest.package.name, new_manifest.package.version
);
}
}
}
}
let vars = PublishDeployAppVariables {
config: serde_json::to_string(&config)?,
name: name.clone().into(),
owner: Some(owner.clone().into()),
};
eprintln!("Publishing app...");
let version = wasmer_api::backend::publish_deploy_app(&client, vars).await?;
eprintln!("App published successfully!");
let url = format!("https://{}.{}.{}.wasmer.app", version.version, name, owner);
eprintln!("Access your app at: {}", url);
self.fmt.format.render(&version);
Ok(())
}
fn load_wasmer_manifest(path: &Path) -> Result<Option<wasmer_toml::Manifest>, anyhow::Error> {
let p = path.join("wasmer.toml");
if p.is_file() {
let contents = std::fs::read_to_string(&p)?;
let manifest = wasmer_toml::Manifest::parse(&contents)
.with_context(|| format!("Could not parse wasmer.toml at: '{}'", p.display()))?;
Ok(Some(manifest))
} else {
Ok(None)
}
}
}
impl AsyncCliCommand for CmdAppPublish {
fn run_async(self) -> futures::future::BoxFuture<'static, Result<(), anyhow::Error>> {
Box::pin(self.run())
}
}