Skip to content

Commit

Permalink
Use resolve_repository_config()
Browse files Browse the repository at this point in the history
  • Loading branch information
cnpryer committed Feb 25, 2024
1 parent 7c8c802 commit 0c3e5d4
Showing 1 changed file with 98 additions and 64 deletions.
162 changes: 98 additions & 64 deletions rye/src/cli/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,90 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
None => vec![project.workspace_path().join("dist").join("*")],
};

// 1. Get username/token/password from arguments
// 1. Or get username/token/password from ~/.rye/credentials keyed by provided repository
// - Prompt decrypt if not --yes
// 1. Or get username/token/password from prompt (if not --yes)
// 1. Otherwise let Twine handle remaining credentials resolution
// 1. Offer encryption if not --skip-save-credentials
//
// We try to do our best to preserve a 'pypi' repo default but allow --repository-url override
let repository = cmd.repository;
let mut repository_url = cmd.repository_url;
let repository_config = resolve_repository_config(
cmd.username,
cmd.token.map(Secret::new),
&cmd.repository,
cmd.repository_url,
&cmd.skip_save_credentials,
&cmd.yes,
)?;

let mut publish_cmd = Command::new(get_venv_python_bin(&venv));

// Build Twine command
publish_cmd
.arg("-mtwine")
.arg("--no-color")
.arg("upload")
.args(files);

// If a username is provided use it, if a password is provided without a username then use __token__ with Twine.
if let Some(usr) = repository_config.username.or(repository_config
.password
.as_ref()
.map(|_| "__token__".to_string()))
{
publish_cmd.arg("--username").arg(usr);
}
if let Some(pwd) = repository_config.password.as_ref() {
publish_cmd.arg("--password").arg(pwd.expose_secret());
}
if let Some(url) = repository_config.repository_url.as_ref() {
publish_cmd.arg("--repository-url").arg(url.to_string());
}
if cmd.sign {
publish_cmd.arg("--sign");
}
if let Some(identity) = cmd.identity {
publish_cmd.arg("--identity").arg(identity);
}
if let Some(cert) = cmd.cert {
publish_cmd.arg("--cert").arg(cert);
}
if cmd.yes {
publish_cmd.arg("--non-interactive");
}

if output == CommandOutput::Quiet {
publish_cmd.stdout(Stdio::null());
publish_cmd.stderr(Stdio::null());
}

let status = publish_cmd.status()?;
if !status.success() {
bail!("failed to publish files");
}

Ok(())
}

struct RepositoryConfig {
username: Option<String>,
password: Option<Secret<String>>,
repository_url: Option<Url>,
}

// 1. Get username/token/password from arguments
// 1. Or get username/token/password from ~/.rye/credentials keyed by provided repository
// - Prompt decrypt if not --yes
// 1. Or get username/token/password from prompt (if not --yes)
// 1. Otherwise let Twine handle remaining credentials resolution
// 1. Offer encryption if not --skip-save-credentials
//
// We try to do our best to preserve a 'pypi' defaults
fn resolve_repository_config(
username: Option<String>,
password: Option<Secret<String>>,
repository: &str,
repository_url: Option<Url>,
skip_save_credentials: &bool,
yes: &bool,
) -> Result<RepositoryConfig, Error> {
let mut repository_url = repository_url;
let mut rye_credentials = get_credentials()?;
let repo_credentials = rye_credentials
.entry(&repository)
.entry(repository)
.or_insert(Item::Table(Table::new()));

// Attempt to resolve url from credentials if we aren't provided with one.
Expand All @@ -113,45 +184,46 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
);
}

let username = cmd.username.or(repo_credentials
let username = username.or(repo_credentials
.get("username")
.map(Item::to_string)
.map(escape_string));

let mut password = cmd.token.map(Secret::new);
let mut passphrase = Secret::new(String::new());
let mut has_prompted_decryption = false;

let mut password = password;

if password.is_none() {
if let Some(it) = repo_credentials
.get("token")
.map(Item::to_string)
.map(escape_string)
.map(Secret::new)
{
if !cmd.yes {
if !yes {
passphrase = prompt_decrypt_passphrase()?;
has_prompted_decryption = true;
}
password = Some(decrypt(&it, &passphrase)?);
}
}

if password.is_none() && repository == DEFAULT_REPOSITORY && !cmd.yes {
if password.is_none() && repository == DEFAULT_REPOSITORY && !yes {
echo!("No access token found, generate one at: https://pypi.org/manage/account/token/");
let token = Secret::new(prompt_for_token()?);
password = Some(token)
};

if !cmd.skip_save_credentials {
if let Some(pwd) = password.as_ref() {
if !skip_save_credentials {
if let Some(it) = password.as_ref() {
// Encryption step can be skipped using an empty passphrase with --yes form cli
if !cmd.yes && !has_prompted_decryption {
passphrase = prompt_encrypt_passphrase()?
if !yes && !has_prompted_decryption {
passphrase = prompt_encrypt_passphrase()?;
}

let maybe_encrypted = encrypt(pwd, passphrase)?;
let maybe_encoded = maybe_encode(pwd, &maybe_encrypted);
let maybe_encrypted = encrypt(it, passphrase)?;
let maybe_encoded = maybe_encode(it, &maybe_encrypted);
repo_credentials["token"] = Item::Value(maybe_encoded.expose_secret().into());
}

Expand All @@ -166,49 +238,11 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
write_credentials(&rye_credentials)?;
}

let mut publish_cmd = Command::new(get_venv_python_bin(&venv));

// Build Twine command
publish_cmd
.arg("-mtwine")
.arg("--no-color")
.arg("upload")
.args(files);

// If a username is provided use it, if a password is provided without a username then use __token__ with Twine.
if let Some(usr) = username.or(password.as_ref().map(|_| "__token__".to_string())) {
publish_cmd.arg("--username").arg(usr);
}
if let Some(pwd) = password.as_ref() {
publish_cmd.arg("--password").arg(pwd.expose_secret());
}
if let Some(url) = repository_url.as_ref() {
publish_cmd.arg("--repository-url").arg(url.to_string());
}
if cmd.sign {
publish_cmd.arg("--sign");
}
if let Some(identity) = cmd.identity {
publish_cmd.arg("--identity").arg(identity);
}
if let Some(cert) = cmd.cert {
publish_cmd.arg("--cert").arg(cert);
}
if cmd.yes {
publish_cmd.arg("--non-interactive");
}

if output == CommandOutput::Quiet {
publish_cmd.stdout(Stdio::null());
publish_cmd.stderr(Stdio::null());
}

let status = publish_cmd.status()?;
if !status.success() {
bail!("failed to publish files");
}

Ok(())
Ok(RepositoryConfig {
username,
password,
repository_url,
})
}

fn prompt_for_token() -> Result<String, Error> {
Expand Down

0 comments on commit 0c3e5d4

Please sign in to comment.