Skip to main content

auths_cli/commands/
publish.rs

1use anyhow::Result;
2use std::path::PathBuf;
3
4use crate::commands::artifact::publish::handle_publish;
5use crate::commands::executable::ExecutableCommand;
6use crate::config::CliConfig;
7
8/// Top-level `auths publish` command: sign and publish a signed artifact attestation.
9#[derive(Debug, clap::Args)]
10#[command(
11    about = "Publish a signed artifact attestation to the Auths registry.",
12    after_help = "Examples:
13  auths publish package.tar.gz                            # Sign and publish
14  auths publish --signature package.tar.gz.auths.json    # Publish existing signature
15  auths publish package.tar.gz --package npm:react@18.3.0
16
17Related:
18  auths sign    — Sign an artifact without publishing
19  auths verify  — Verify a signed artifact"
20)]
21pub struct PublishCommand {
22    /// Artifact file to sign and publish. Omit if providing --signature directly.
23    #[arg(help = "Artifact file to sign and publish.")]
24    pub file: Option<PathBuf>,
25
26    /// Path to an existing .auths.json signature file. Defaults to <FILE>.auths.json.
27    #[arg(long, value_name = "PATH")]
28    pub signature: Option<PathBuf>,
29
30    /// Package identifier for registry indexing (e.g., npm:react@18.3.0).
31    #[arg(long)]
32    pub package: Option<String>,
33
34    /// Registry URL to publish to.
35    #[arg(long, env = "AUTHS_REGISTRY_URL")]
36    pub registry: Option<String>,
37}
38
39impl ExecutableCommand for PublishCommand {
40    fn execute(&self, ctx: &CliConfig) -> Result<()> {
41        let sig_path = match (&self.signature, &self.file) {
42            (Some(sig), _) => sig.clone(),
43            (None, Some(file)) => {
44                let mut p = file.clone();
45                p.set_file_name(format!(
46                    "{}.auths.json",
47                    p.file_name().unwrap_or_default().to_string_lossy()
48                ));
49                if !p.exists() {
50                    crate::commands::artifact::sign::handle_sign(
51                        file,
52                        None,
53                        None,
54                        &crate::commands::key_detect::auto_detect_device_key(
55                            ctx.repo_path.as_deref(),
56                            &ctx.env_config,
57                        )?,
58                        None,
59                        None,
60                        // Auto-signing a file for publish does not bind the ambient git HEAD: it is
61                        // unrelated to the file. Bind one with `auths artifact sign --commit <sha>`.
62                        None,
63                        ctx.repo_path.clone(),
64                        ctx.passphrase_provider.clone(),
65                        &ctx.env_config,
66                        &None,
67                        false,
68                        false,
69                    )?;
70                }
71                p
72            }
73            (None, None) => anyhow::bail!("Provide an artifact file or --signature path"),
74        };
75
76        let registry = crate::commands::verify_helpers::require_registry(self.registry.clone())?;
77        handle_publish(&sig_path, self.package.as_deref(), &registry)
78    }
79}