Skip to content

Commit 25e3fee

Browse files
committed
Allow using 'credentials.toml' instead of just 'credentials' files.
This matches a similar change to config[.toml]. Note that this change only makes 'credentials.toml' optional to use instead of 'credentials'. If both exist, we will print a warning and prefer 'credentials', since that would be the existing behavior if both existed.
1 parent c530720 commit 25e3fee

File tree

2 files changed

+101
-41
lines changed

2 files changed

+101
-41
lines changed

src/cargo/util/config.rs

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -689,56 +689,58 @@ impl Config {
689689
}
690690
}
691691

692+
/// The purpose of this function is to aid in the transition to using
693+
/// .toml extensions on Cargo's config files, which were historically not used.
694+
/// Both 'config.toml' and 'credentials.toml' should be valid with or without extension.
695+
/// When both exist, we want to prefer the one without an extension for
696+
/// backwards compatibility, but warn the user appropriately.
697+
fn get_file_path(
698+
&self,
699+
dir: &Path,
700+
filename_without_extension: &str,
701+
warn: bool,
702+
) -> CargoResult<Option<PathBuf>> {
703+
let possible = dir.join(filename_without_extension);
704+
let possible_with_extension = dir.join(format!("{}.toml", filename_without_extension));
705+
706+
if fs::metadata(&possible).is_ok() {
707+
if warn && fs::metadata(&possible_with_extension).is_ok() {
708+
self.shell().warn(format!(
709+
"Both `{}` and `{}` exist. Using `{}`",
710+
possible.display(),
711+
possible_with_extension.display(),
712+
possible.display()
713+
))?;
714+
}
715+
716+
Ok(Some(possible))
717+
} else if fs::metadata(&possible_with_extension).is_ok() {
718+
Ok(Some(possible_with_extension))
719+
} else {
720+
Ok(None)
721+
}
722+
}
723+
692724
fn walk_tree<F>(&self, pwd: &Path, home: &Path, mut walk: F) -> CargoResult<()>
693725
where
694726
F: FnMut(&Path) -> CargoResult<()>,
695727
{
696728
let mut stash: HashSet<PathBuf> = HashSet::new();
697729

698730
for current in paths::ancestors(pwd) {
699-
let possible = current.join(".cargo").join("config");
700-
let possible_with_extension = current.join(".cargo").join("config.toml");
701-
702-
// If both 'config' and 'config.toml' exist, we should use 'config'
703-
// for backward compatibility, but we should warn the user.
704-
if fs::metadata(&possible).is_ok() {
705-
if fs::metadata(&possible_with_extension).is_ok() {
706-
self.shell().warn(format!(
707-
"Both `{}` and `{}` exist. Using `{}`",
708-
possible.display(),
709-
possible_with_extension.display(),
710-
possible.display()
711-
))?;
712-
}
713-
714-
walk(&possible)?;
715-
stash.insert(possible);
716-
} else if fs::metadata(&possible_with_extension).is_ok() {
717-
walk(&possible_with_extension)?;
718-
stash.insert(possible);
731+
if let Some(path) = self.get_file_path(&current.join(".cargo"), "config", true)? {
732+
walk(&path)?;
733+
stash.insert(path);
719734
}
720735
}
721736

722737
// Once we're done, also be sure to walk the home directory even if it's not
723738
// in our history to be sure we pick up that standard location for
724739
// information.
725-
let config = home.join("config");
726-
let config_with_extension = home.join("config.toml");
727-
if !stash.contains(&config) && fs::metadata(&config).is_ok() {
728-
if fs::metadata(&config_with_extension).is_ok() {
729-
self.shell().warn(format!(
730-
"Both `{}` and `{}` exist. Using `{}`",
731-
config.display(),
732-
config_with_extension.display(),
733-
config.display()
734-
))?;
740+
if let Some(path) = self.get_file_path(home, "config", true)? {
741+
if !stash.contains(&path) {
742+
walk(&path)?;
735743
}
736-
737-
walk(&config)?;
738-
} else if !stash.contains(&config_with_extension)
739-
&& fs::metadata(&config_with_extension).is_ok()
740-
{
741-
walk(&config_with_extension)?;
742744
}
743745

744746
Ok(())
@@ -781,10 +783,10 @@ impl Config {
781783
/// present.
782784
fn load_credentials(&self, cfg: &mut ConfigValue) -> CargoResult<()> {
783785
let home_path = self.home_path.clone().into_path_unlocked();
784-
let credentials = home_path.join("credentials");
785-
if fs::metadata(&credentials).is_err() {
786-
return Ok(());
787-
}
786+
let credentials = match self.get_file_path(&home_path, "credentials", true)? {
787+
Some(credentials) => credentials,
788+
None => return Ok(()),
789+
};
788790

789791
let mut contents = String::new();
790792
let mut file = File::open(&credentials)?;
@@ -1729,10 +1731,22 @@ pub fn homedir(cwd: &Path) -> Option<PathBuf> {
17291731
}
17301732

17311733
pub fn save_credentials(cfg: &Config, token: String, registry: Option<String>) -> CargoResult<()> {
1734+
// If 'credentials.toml' exists, we should write to that, otherwise
1735+
// use the legacy 'credentials'. There's no need to print the warning
1736+
// here, because it would already be printed at load time.
1737+
let home_path = cfg.home_path.clone().into_path_unlocked();
1738+
let filename = match cfg.get_file_path(&home_path, "credentials", false)? {
1739+
Some(path) => match path.file_name() {
1740+
Some(filename) => Path::new(filename).to_owned(),
1741+
None => Path::new("credentials").to_owned(),
1742+
},
1743+
None => Path::new("credentials").to_owned(),
1744+
};
1745+
17321746
let mut file = {
17331747
cfg.home_path.create_dir()?;
17341748
cfg.home_path
1735-
.open_rw(Path::new("credentials"), cfg, "credentials' config file")?
1749+
.open_rw(filename, cfg, "credentials' config file")?
17361750
};
17371751

17381752
let (key, value) = {

tests/testsuite/login.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fs::{self, File};
22
use std::io::prelude::*;
3+
use std::path::PathBuf;
34

45
use crate::support::cargo_process;
56
use crate::support::install::cargo_home;
@@ -13,6 +14,15 @@ const ORIGINAL_TOKEN: &str = "api-token";
1314

1415
fn setup_new_credentials() {
1516
let config = cargo_home().join("credentials");
17+
setup_new_credentials_at(config);
18+
}
19+
20+
fn setup_new_credentials_toml() {
21+
let config = cargo_home().join("credentials.toml");
22+
setup_new_credentials_at(config);
23+
}
24+
25+
fn setup_new_credentials_at(config: PathBuf) {
1626
t!(fs::create_dir_all(config.parent().unwrap()));
1727
t!(t!(File::create(&config))
1828
.write_all(format!(r#"token = "{token}""#, token = ORIGINAL_TOKEN).as_bytes()));
@@ -84,6 +94,42 @@ fn login_with_new_credentials() {
8494
assert!(check_token(TOKEN, None));
8595
}
8696

97+
#[cargo_test]
98+
fn credentials_work_with_extension() {
99+
registry::init();
100+
setup_new_credentials_toml();
101+
102+
cargo_process("login --host")
103+
.arg(registry_url().to_string())
104+
.arg(TOKEN)
105+
.run();
106+
107+
// Ensure that we get the new token for the registry
108+
assert!(check_token(TOKEN, None));
109+
}
110+
111+
#[cargo_test]
112+
fn credentials_ambiguous_filename() {
113+
registry::init();
114+
setup_new_credentials();
115+
setup_new_credentials_toml();
116+
117+
cargo_process("login --host")
118+
.arg(registry_url().to_string())
119+
.arg(TOKEN)
120+
.with_stderr_contains(
121+
"\
122+
[WARNING] Both `[..]/credentials` and `[..]/credentials.toml` exist. Using `[..]/credentials`
123+
",
124+
)
125+
.run();
126+
127+
// It should use the value from the one without the extension
128+
// for backwards compatibility. check_token explicitly checks
129+
// 'credentials' without the extension, which is what we want.
130+
assert!(check_token(TOKEN, None));
131+
}
132+
87133
#[cargo_test]
88134
fn login_with_old_and_new_credentials() {
89135
setup_new_credentials();

0 commit comments

Comments
 (0)