1use clap::Args;
2use lux_lib::{
3 config::Config, package::PackageName, remote_package_db::RemotePackageDB,
4 upload::ProjectUpload, workspace::Workspace,
5};
6
7use miette::{IntoDiagnostic, Result};
8
9#[cfg(feature = "gpgme")]
10use lux_lib::upload::SignatureProtocol;
11
12#[derive(Args)]
13pub struct Upload {
14 #[cfg(feature = "gpgme")]
16 #[arg(long, default_value_t)]
17 sign_protocol: SignatureProtocol,
18
19 #[arg(short, long, visible_short_alias = 'p')]
21 package: Option<PackageName>,
22
23 #[arg(short, long, visible_short_alias = 'c')]
27 tfa_code: Option<String>,
28}
29
30#[cfg(feature = "gpgme")]
31pub async fn upload(data: Upload, config: Config) -> Result<()> {
32 let workspace = Workspace::current_or_err()?;
33
34 let package_db = RemotePackageDB::from_config(&config).await?;
35 let tfa_code = tfa_code_from_args_or_secret(&data)?;
36 if let Some(package) = data.package {
37 let project = workspace.select_member(&package)?;
38 ProjectUpload::new()
39 .project(project)
40 .config(&config)
41 .sign_protocol(data.sign_protocol.clone())
42 .maybe_tfa_code(tfa_code)
43 .package_db(&package_db)
44 .upload_to_luarocks()
45 .await?;
46 } else {
47 for project in workspace.members() {
48 ProjectUpload::new()
49 .project(project)
50 .config(&config)
51 .sign_protocol(data.sign_protocol.clone())
52 .maybe_tfa_code(tfa_code.clone())
53 .package_db(&package_db)
54 .upload_to_luarocks()
55 .await?;
56 }
57 }
58
59 Ok(())
60}
61
62#[cfg(not(feature = "gpgme"))]
63pub async fn upload(data: Upload, config: Config) -> Result<()> {
64 let workspace = Workspace::current_or_err()?;
65 let package_db = RemotePackageDB::from_config(&config).await?;
66 let tfa_code = tfa_code_from_args_or_secret(&data)?;
67 if let Some(package) = data.package {
68 let project = workspace.select_member(&package)?;
69 ProjectUpload::new()
70 .project(project)
71 .config(&config)
72 .maybe_tfa_code(tfa_code)
73 .package_db(&package_db)
74 .upload_to_luarocks()
75 .await?;
76 } else {
77 for project in workspace.members() {
78 ProjectUpload::new()
79 .project(project)
80 .config(&config)
81 .maybe_tfa_code(tfa_code.clone())
82 .package_db(&package_db)
83 .upload_to_luarocks()
84 .await?;
85 }
86 }
87
88 Ok(())
89}
90
91fn tfa_code_from_args_or_secret(data: &Upload) -> Result<Option<String>> {
92 match &data.tfa_code {
93 Some(code) => Ok(Some(code.to_owned())),
94 None => match std::env::var("LUAROCKS_2FA_SECRET") {
95 Ok(secret) => {
96 let secret = base32::decode(base32::Alphabet::Crockford, &secret)
97 .ok_or(totp_rs::SecretParseError::ParseBase32)
98 .into_diagnostic()?;
99 let totp = totp_rs::TOTP::new(totp_rs::Algorithm::SHA1, 6, 1, 30, secret)
100 .into_diagnostic()?;
101 Ok(Some(totp.generate_current().into_diagnostic()?))
102 }
103 Err(_) => Ok(None),
104 },
105 }
106}