wasmer_cli/commands/package/
publish.rs

1use crate::{
2    commands::{
3        AsyncCliCommand,
4        package::{
5            common::{wait::*, *},
6            push::PackagePush,
7            tag::PackageTag,
8        },
9    },
10    config::WasmerEnv,
11};
12use colored::Colorize;
13use is_terminal::IsTerminal;
14use std::path::{Path, PathBuf};
15use wasmer_backend_api::WasmerClient;
16use wasmer_config::package::{Manifest, PackageIdent};
17
18/// Publish (push and tag) a package to the registry.
19#[derive(Debug, clap::Parser)]
20pub struct PackagePublish {
21    #[clap(flatten)]
22    pub env: WasmerEnv,
23
24    /// Run the publish logic without sending anything to the registry server
25    #[clap(long, name = "dry-run")]
26    pub dry_run: bool,
27
28    /// Run the publish command without any output
29    #[clap(long)]
30    pub quiet: bool,
31
32    /// Override the namespace of the package to upload
33    #[clap(long = "namespace")]
34    pub package_namespace: Option<String>,
35
36    /// Override the name of the package to upload
37    #[clap(long = "name")]
38    pub package_name: Option<String>,
39
40    /// Override the package version of the uploaded package in the wasmer.toml
41    #[clap(long = "version")]
42    pub package_version: Option<semver::Version>,
43
44    /// Skip validation of the uploaded package
45    #[clap(long)]
46    pub no_validate: bool,
47
48    /// Directory containing the `wasmer.toml`, or a custom *.toml manifest file.
49    ///
50    /// Defaults to current working directory.
51    #[clap(name = "path", default_value = ".")]
52    pub package_path: PathBuf,
53
54    /// Wait for package to be available on the registry before exiting.
55    #[clap(
56            long,
57            require_equals = true,
58            num_args = 0..=1,
59            default_value_t = PublishWait::None,
60            default_missing_value = "container",
61            value_enum
62        )]
63    pub wait: PublishWait,
64
65    /// Timeout (in seconds) for the publish query to the registry.
66    ///
67    /// Note that this is not the timeout for the entire publish process, but
68    /// for each individual query to the registry during the publish flow.
69    #[clap(long, default_value = "5m")]
70    pub timeout: humantime::Duration,
71
72    /// Whether or not the patch field of the version of the package - if any - should be bumped.
73    #[clap(long, conflicts_with = "package_version")]
74    pub bump: bool,
75
76    /// Do not prompt for user input.
77    #[clap(long, default_value_t = !std::io::stdin().is_terminal())]
78    pub non_interactive: bool,
79}
80
81impl PackagePublish {
82    pub async fn publish(
83        &self,
84        client: &WasmerClient,
85        manifest_path: &Path,
86        manifest: &Manifest,
87        allow_unnamed: bool,
88    ) -> anyhow::Result<PackageIdent> {
89        let (package_namespace, package_hash) = {
90            let push_cmd = PackagePush {
91                env: self.env.clone(),
92                dry_run: self.dry_run,
93                quiet: self.quiet,
94                package_name: self.package_name.clone(),
95                package_namespace: self.package_namespace.clone(),
96                timeout: self.timeout,
97                non_interactive: self.non_interactive,
98                package_path: self.package_path.clone(),
99            };
100
101            push_cmd.push(client, manifest, manifest_path).await?
102        };
103
104        PackageTag {
105            wait: self.wait,
106            env: self.env.clone(),
107            dry_run: self.dry_run,
108            quiet: self.quiet,
109            package_namespace: Some(package_namespace),
110            package_name: self.package_name.clone(),
111            package_version: self.package_version.clone(),
112            timeout: self.timeout,
113            bump: self.bump,
114            non_interactive: self.non_interactive,
115            package_path: self.package_path.clone(),
116            package_hash,
117            package_id: None,
118        }
119        .tag(
120            client,
121            Some(manifest),
122            Some(manifest_path),
123            true,
124            allow_unnamed,
125        )
126        .await
127    }
128}
129
130#[async_trait::async_trait]
131impl AsyncCliCommand for PackagePublish {
132    type Output = PackageIdent;
133
134    async fn run_async(self) -> Result<Self::Output, anyhow::Error> {
135        tracing::info!("Checking if user is logged in");
136        let client = login_user(&self.env, !self.non_interactive, "publish a package").await?;
137
138        tracing::info!("Loading manifest");
139        let (manifest_path, manifest) = get_manifest(&self.package_path)?;
140        tracing::info!("Got manifest at path {}", manifest_path.display());
141
142        let ident = self
143            .publish(&client, &manifest_path, &manifest, false)
144            .await?;
145
146        match ident {
147            PackageIdent::Named(ref n) => {
148                let url = make_package_url(&client, n);
149                eprintln!("\n{} Package URL: {url}", "ð–¥”".yellow().bold());
150            }
151            PackageIdent::Hash(ref h) => {
152                eprintln!(
153                    "\n{} Succesfully published package ({h})",
154                    "✔".green().bold()
155                );
156            }
157        }
158
159        Ok(ident)
160    }
161}