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