feat: Add `bug-report` sub-command (#725)

This adds a sub command to generate the link. Information, such as operating system and it's version; the current shell's config; and current starship conf, is gathered from the environment and is included in the pre-filled text. The command will also try to open the link in the default browser. Should that fail it will print the link instead and ask the user to copy it.
This commit is contained in:
Jon Grythe Stødle 2019-12-15 00:40:12 +01:00 committed by Matan Kushner
parent 740b51bd60
commit 76804cc3c8
5 changed files with 1195 additions and 45 deletions

View File

@ -8,6 +8,7 @@ assignees: ''
---
## Bug Report
<!-- We recommend running `starship bug-report` in the shell you are experiencing unexpected behavior. The command will automatically pre-fill a report with your system information. -->
#### Current Behavior
<!-- A clear and concise description of the behavior. -->

921
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -46,6 +46,10 @@ byte-unit = "3.0.3"
starship_module_config_derive = { version = "0.1.0", path = "starship_module_config_derive" }
yaml-rust = "0.4"
nom = "5.0.1"
os_info = "1.1.3"
urlencoding = "1.0.0"
open = "1.3.2"
reqwest = "0.9.24"
[dev-dependencies]
tempfile = "3.1.0"

218
src/bug_report.rs Normal file
View File

@ -0,0 +1,218 @@
use crate::utils::exec_cmd;
use reqwest;
use std::fs;
use std::path::PathBuf;
const GIT_IO_BASE_URL: &str = "https://git.io/";
pub fn create() {
let os_info = os_info::get();
let environment = Environment {
os_type: os_info.os_type(),
os_version: os_info.version().to_owned(),
shell_info: get_shell_info(),
starship_config: get_starship_config(),
};
let link = make_github_issue_link(crate_version!(), environment);
if open::that(&link).is_ok() {
print!("Take a look at your browser. A GitHub issue has been populated with your configuration")
} else {
let link = reqwest::Client::new()
.post(&format!("{}{}", GIT_IO_BASE_URL, "create"))
.form(&[("url", &link)])
.send()
.and_then(|mut response| response.text())
.map(|slug| format!("{}{}", GIT_IO_BASE_URL, slug))
.unwrap_or(link);
println!(
"Click this link to create a GitHub issue populated with your configuration:\n\n {}",
link
);
}
}
const UNKNOWN_SHELL: &str = "<unknown shell>";
const UNKNOWN_VERSION: &str = "<unknown version>";
const UNKNOWN_CONFIG: &str = "<unknown config>";
struct Environment {
os_type: os_info::Type,
os_version: os_info::Version,
shell_info: ShellInfo,
starship_config: String,
}
fn make_github_issue_link(starship_version: &str, environment: Environment) -> String {
let title = urlencoding::encode("Bug Report:");
let body = urlencoding::encode(&format!("<!--
This issue has been pre-populated with your system's configuration
Thank you for submitting a bug report
-->
## Bug Report
#### Current Behavior
<!-- A clear and concise description of the behavior. -->
#### Expected Behavior
<!-- A clear and concise description of what you expected to happen. -->
#### Additional context/Screenshots
<!-- Add any other context about the problem here. If applicable, add screenshots to help explain. -->
#### Environment
- Starship version: {starship_version}
- {shell_name} version: {shell_version}
- Operating system: {os_name} {os_version}
#### Relevant Shell Configuration
```bash
{shell_config}
```
#### Starship Configuration
```toml
{starship_config}
```
#### Possible Solution
<!--- Only if you have suggestions on a fix for the bug -->",
starship_version = starship_version,
shell_name = environment.shell_info.name,
shell_version = environment.shell_info.version,
os_name = environment.os_type,
os_version = environment.os_version,
shell_config = environment.shell_info.config,
starship_config = environment.starship_config,
));
format!(
"https://github.com/starship/starship/issues/new?title={}&body={}",
title, body
)
}
#[derive(Debug)]
struct ShellInfo {
name: String,
version: String,
config: String,
}
fn get_shell_info() -> ShellInfo {
let shell = std::env::var("STARSHIP_SHELL");
if shell.is_err() {
return ShellInfo {
name: UNKNOWN_SHELL.to_string(),
version: UNKNOWN_VERSION.to_string(),
config: UNKNOWN_CONFIG.to_string(),
};
}
let shell = shell.unwrap();
let version = exec_cmd(&shell, &["--version"])
.map(|output| output.stdout.trim().to_string())
.unwrap_or_else(|| UNKNOWN_VERSION.to_string());
let config = get_config_path(&shell)
.and_then(|config_path| fs::read_to_string(config_path).ok())
.map(|config| config.trim().to_string())
.unwrap_or_else(|| UNKNOWN_CONFIG.to_string());
ShellInfo {
name: shell,
version,
config,
}
}
fn get_config_path(shell: &str) -> Option<PathBuf> {
dirs::home_dir().and_then(|home_dir| {
match shell {
"bash" => Some(".bashrc"),
"fish" => Some(".config/fish/config.fish"),
"ion" => Some("~/.config/ion/initrc"),
"powershell" => {
if cfg!(windows) {
Some("Documents/PowerShell/Microsoft.PowerShell_profile.ps1")
} else {
Some(".config/powershell/Microsoft.PowerShell_profile.ps1")
}
}
"zsh" => Some(".zshrc"),
_ => None,
}
.map(|path| home_dir.join(path))
})
}
fn get_starship_config() -> String {
dirs::home_dir()
.and_then(|home_dir| fs::read_to_string(home_dir.join(".config/starship.toml")).ok())
.unwrap_or_else(|| UNKNOWN_CONFIG.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
use os_info;
use std::env;
#[test]
fn test_make_github_issue_link() {
let starship_version = "0.1.2";
let environment = Environment {
os_type: os_info::Type::Linux,
os_version: os_info::Version::semantic(1, 2, 3, Some("test".to_string())),
shell_info: ShellInfo {
name: "test_shell".to_string(),
version: "2.3.4".to_string(),
config: "No config".to_string(),
},
starship_config: "No Starship config".to_string(),
};
let link = make_github_issue_link(starship_version, environment);
assert!(link.contains(starship_version));
assert!(link.contains("Linux"));
assert!(link.contains("1.2.3"));
assert!(link.contains("test_shell"));
assert!(link.contains("2.3.4"));
assert!(link.contains("No%20config"));
assert!(link.contains("No%20Starship%20config"));
}
#[test]
fn test_get_shell_info() {
env::remove_var("STARSHIP_SHELL");
let unknown_shell = get_shell_info();
assert_eq!(UNKNOWN_SHELL, &unknown_shell.name);
env::set_var("STARSHIP_SHELL", "fish");
let fish_shell = get_shell_info();
assert_eq!("fish", &fish_shell.name);
}
#[test]
#[cfg(not(windows))]
fn test_get_config_path() {
env::set_var("HOME", "/test/home");
let config_path = get_config_path("bash");
assert_eq!("/test/home/.bashrc", config_path.unwrap().to_str().unwrap());
}
}

View File

@ -1,6 +1,7 @@
#[macro_use]
extern crate clap;
mod bug_report;
mod config;
mod configs;
mod context;
@ -64,51 +65,55 @@ fn main() {
.long("print-full-init")
.help("Print the main initialization script (as opposed to the init stub)");
let matches = App::new("starship")
.about("The cross-shell prompt for astronauts. ☄🌌️")
// pull the version number from Cargo.toml
.version(crate_version!())
// pull the authors from Cargo.toml
.author(crate_authors!())
.after_help("https://github.com/starship/starship")
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("init")
.about("Prints the shell function used to execute starship")
.arg(&shell_arg)
.arg(&init_scripts_arg),
)
.subcommand(
SubCommand::with_name("prompt")
.about("Prints the full starship prompt")
.arg(&status_code_arg)
.arg(&path_arg)
.arg(&cmd_duration_arg)
.arg(&keymap_arg)
.arg(&jobs_arg),
)
.subcommand(
SubCommand::with_name("module")
.about("Prints a specific prompt module")
.arg(
Arg::with_name("name")
.help("The name of the module to be printed")
.required(true)
.required_unless("list"),
)
.arg(
Arg::with_name("list")
.short("l")
.long("list")
.help("List out all supported modules"),
)
.arg(&status_code_arg)
.arg(&path_arg)
.arg(&cmd_duration_arg)
.arg(&keymap_arg)
.arg(&jobs_arg),
)
.get_matches();
let matches =
App::new("starship")
.about("The cross-shell prompt for astronauts. ☄🌌️")
// pull the version number from Cargo.toml
.version(crate_version!())
// pull the authors from Cargo.toml
.author(crate_authors!())
.after_help("https://github.com/starship/starship")
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("init")
.about("Prints the shell function used to execute starship")
.arg(&shell_arg)
.arg(&init_scripts_arg),
)
.subcommand(
SubCommand::with_name("prompt")
.about("Prints the full starship prompt")
.arg(&status_code_arg)
.arg(&path_arg)
.arg(&cmd_duration_arg)
.arg(&keymap_arg)
.arg(&jobs_arg),
)
.subcommand(
SubCommand::with_name("module")
.about("Prints a specific prompt module")
.arg(
Arg::with_name("name")
.help("The name of the module to be printed")
.required(true)
.required_unless("list"),
)
.arg(
Arg::with_name("list")
.short("l")
.long("list")
.help("List out all supported modules"),
)
.arg(&status_code_arg)
.arg(&path_arg)
.arg(&cmd_duration_arg)
.arg(&keymap_arg)
.arg(&jobs_arg),
)
.subcommand(SubCommand::with_name("bug-report").about(
"Create a pre-populated GitHub issue with information about your configuration",
))
.get_matches();
match matches.subcommand() {
("init", Some(sub_m)) => {
@ -132,6 +137,7 @@ fn main() {
print::module(module_name, sub_m.clone());
}
}
("bug-report", Some(_)) => bug_report::create(),
_ => {}
}
}