Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Houston, we have some suggestions #204

Merged
merged 1 commit into from
Jan 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ repository = "https://github.com/apollographql/rover/"
[dependencies]

# workspace deps
binstall = { path = "./installers/binstall" }
houston = { path = "./crates/houston" }
robot-panic = { path = "./crates/robot-panic" }
rover-client = { path = "./crates/rover-client" }
sputnik = { path = "./crates/sputnik" }
timber = { path = "./crates/timber" }
robot-panic = { path = "./crates/robot-panic" }
binstall = { path = "./installers/binstall" }

# crates.io deps
anyhow = "1.0.38"
Expand Down
37 changes: 24 additions & 13 deletions crates/houston/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,32 @@ impl Config {
override_api_key: Option<String>,
) -> Result<Config, HoustonProblem> {
let home = match override_home {
Some(home) => PathBuf::from(home.as_ref()),
Some(home) => {
let home_path = PathBuf::from(home.as_ref());
if home_path.exists() && !home_path.is_dir() {
Err(HoustonProblem::InvalidOverrideConfigDir(
home_path.display().to_string(),
))
} else {
Ok(home_path)
}
}
None => {
// Lin: /home/alice/.config/rover
// Win: C:\Users\Alice\AppData\Roaming\Apollo\Rover\config
// Mac: /Users/Alice/Library/Application Support/com.Apollo.Rover
ProjectDirs::from("com", "Apollo", "Rover")
.ok_or(HoustonProblem::ConfigDirNotFound)?
Ok(ProjectDirs::from("com", "Apollo", "Rover")
.ok_or(HoustonProblem::DefaultConfigDirNotFound)?
.config_dir()
.to_path_buf()
.to_path_buf())
}
};
}?;

if !home.exists() {
fs::create_dir_all(&home).map_err(|_| {
HoustonProblem::CouldNotCreateConfigHome(home.display().to_string())
})?;
}

Ok(Config {
home,
Expand All @@ -47,7 +62,8 @@ impl Config {
/// Removes all configuration files from filesystem
pub fn clear(&self) -> Result<(), HoustonProblem> {
tracing::debug!(home_dir = ?self.home);
fs::remove_dir_all(&self.home).map_err(|_| HoustonProblem::NoConfigFound)
fs::remove_dir_all(&self.home)
.map_err(|_| HoustonProblem::NoConfigFound(self.home.display().to_string()))
}
}

Expand All @@ -57,15 +73,10 @@ mod tests {
use assert_fs::TempDir;
#[test]
fn it_can_clear_global_config() {
let config = get_config(None);
std::fs::create_dir(&config.home).unwrap();
let tmp_home = TempDir::new().unwrap();
let config = Config::new(Some(&tmp_home.path()), None).unwrap();
assert!(config.home.exists());
config.clear().unwrap();
assert_eq!(config.home.exists(), false);
}

fn get_config(override_api_key: Option<String>) -> Config {
let tmp_home = TempDir::new().unwrap();
Config::new(Some(&tmp_home.path()), override_api_key).unwrap()
}
}
20 changes: 14 additions & 6 deletions crates/houston/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,27 @@ use std::io;
#[derive(Error, Debug)]
pub enum HoustonProblem {
/// ConfigDirNotFound occurs when the default OS config can't be found.
#[error("Could not determine default OS config directory.")]
ConfigDirNotFound,
#[error("Could not determine default OS configuration directory.")]
DefaultConfigDirNotFound,

/// CouldNotCreateConfig occurs when a configuration directory could not be created.
#[error("Could not create a configuration directory at \"{0}\".")]
CouldNotCreateConfigHome(String),

/// InvalidOverrideConfigDir occurs when a user provides a path to a non-directory.
#[error("\"{0}\" already exists and is not a directory.")]
InvalidOverrideConfigDir(String),

/// NoConfigFound occurs when a global configuration directory can't be found.
#[error("Could not find a global configuration directory.")]
NoConfigFound,
#[error("Could not find a configuration directory at \"{0}\".")]
NoConfigFound(String),

/// ProfileNotFound occurs when a profile with a specified name can't be found.
#[error("There is no profile named \"{0}\"")]
#[error("There is no profile named \"{0}\".")]
ProfileNotFound(String),

/// NoNonSensitiveConfigFound occurs when non-sensitive config can't be found for a profile.
#[error("No non-sensitive config found for profile \"{0}\"")]
#[error("No non-sensitive configuration found for profile \"{0}\".")]
NoNonSensitiveConfigFound(String),

/// TomlSerialization occurs when a profile's configuration can't be serialized to a String.
Expand Down
1 change: 1 addition & 0 deletions src/error/metadata/code.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::fmt::{self, Display};

/// `Code` contains the error codes associated with specific errors.
#[derive(Debug)]
pub enum Code {}

Expand Down
29 changes: 28 additions & 1 deletion src/error/metadata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,22 @@ use suggestion::Suggestion;
use houston::HoustonProblem;
use rover_client::RoverClientError;

use crate::env::RoverEnvKey;

use std::env;

/// Metadata contains extra information about specific errors
/// Currently this includes an optional error `Code`
/// and an optional `Suggestion`
#[derive(Default, Debug)]
pub struct Metadata {
pub suggestion: Option<Suggestion>,
pub code: Option<Code>,
}

/// `Metadata` structs can be created from an `anyhow::Error`
/// This works by downcasting the errors to their underlying types
/// and creating `Suggestion`s and `Code`s where applicable
impl From<&mut anyhow::Error> for Metadata {
fn from(error: &mut anyhow::Error) -> Self {
if let Some(rover_client_error) = error.downcast_ref::<RoverClientError>() {
Expand All @@ -33,7 +43,24 @@ impl From<&mut anyhow::Error> for Metadata {
HoustonProblem::NoNonSensitiveConfigFound(_) => {
(Some(Suggestion::RerunWithSensitive), None)
}
_ => (None, None),
HoustonProblem::CouldNotCreateConfigHome(_)
| HoustonProblem::DefaultConfigDirNotFound
| HoustonProblem::InvalidOverrideConfigDir(_) => {
(Some(Suggestion::SetConfigHome), None)
}
HoustonProblem::NoConfigFound(_) => {
let code = None;
let suggestion = if env::var_os(RoverEnvKey::ConfigHome.to_string()).is_some() {
Some(Suggestion::MigrateConfigHomeOrCreateConfig)
} else {
Some(Suggestion::CreateConfig)
};
(suggestion, code)
}
HoustonProblem::ProfileNotFound(_) => (Some(Suggestion::ListProfiles), None),
HoustonProblem::TomlDeserialization(_)
| HoustonProblem::TomlSerialization(_)
| HoustonProblem::IOError(_) => (Some(Suggestion::SubmitIssue), None),
};
return Metadata { suggestion, code };
}
Expand Down
31 changes: 31 additions & 0 deletions src/error/metadata/suggestion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ use std::fmt::{self, Display};

use ansi_term::Colour::{Cyan, Yellow};

use crate::env::RoverEnvKey;

/// `Suggestion` contains possible suggestions for remedying specific errors.
#[derive(Debug)]
pub enum Suggestion {
SubmitIssue,
RerunWithSensitive,
SetConfigHome,
MigrateConfigHomeOrCreateConfig,
CreateConfig,
ListProfiles,
}

impl Display for Suggestion {
Expand All @@ -20,6 +27,30 @@ impl Display for Suggestion {
Yellow.normal().paint("'--sensitive'")
)
}
Suggestion::SetConfigHome => {
format!(
"You can override this path by setting the {} environment variable.",
Yellow.normal().paint(RoverEnvKey::ConfigHome.to_string())
)
}
Suggestion::MigrateConfigHomeOrCreateConfig => {
format!("If you've recently changed the {} environment variable, you may need to migrate your old configuration directory to the new path. Otherwise, try setting up a new configuration profile by running {}.",
Yellow.normal().paint(RoverEnvKey::ConfigHome.to_string()),
Yellow.normal().paint("`rover config auth`"))
}
Suggestion::CreateConfig => {
format!(
"Try setting up a configuration profile by running {}",
Yellow.normal().paint("`rover config auth`")
)
}
Suggestion::ListProfiles => {
format!(
"Try running {} to see the possible values for the {} argument.",
Yellow.normal().paint("`rover config list`"),
Yellow.normal().paint("'--profile'")
)
}
};
write!(formatter, "{}", &suggestion)
}
Expand Down
6 changes: 2 additions & 4 deletions tests/config/profile.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use assert_cmd::Command;
use assert_fs::TempDir;
use predicates::prelude::*;
use serial_test::serial;
use std::path::PathBuf;

use houston::{Config, Profile};
Expand All @@ -11,22 +10,21 @@ const CUSTOM_PROFILE: &str = "custom-profile";
const CUSTOM_API_KEY: &str = "custom-api-key";

#[test]
#[serial]
fn it_can_list_no_profiles() {
let temp_dir = get_temp_dir();
let mut cmd = Command::cargo_bin("rover").unwrap();
let result = cmd
.arg("config")
.env(
RoverEnvKey::ConfigHome.to_string(),
get_temp_dir().to_string_lossy().to_string(),
temp_dir.to_string_lossy().to_string(),
)
.arg("list")
.assert();
result.stderr(predicate::str::contains("No profiles"));
}

#[test]
#[serial]
fn it_can_list_one_profile() {
let temp_dir = get_temp_dir();
let config = Config::new(Some(temp_dir.clone()).as_ref(), None).unwrap();
Expand Down