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

Doctor command for Health check #393

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions crates/web5_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ tokio = { version = "1.38.0", features = ["full"] }
web5 = { path = "../web5" }
url = "2.5.2"
uuid = { workspace = true }
reqwest = "0.12.8"
colored = "2.1.0"
25 changes: 25 additions & 0 deletions crates/web5_cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,28 @@ web5 vc create "alice" --portable-did $PORTABLE_DID

web5 vc verify eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpkaHQ6OXFnOGgxc3Jvd2hzZHNla3hwODk4eTU0MXhndGZ4Ym1ybW5oaGdzanlobXRtOHRjb253byMwIn0.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJpZCI6InVybjp2Yzp1dWlkOjlkMDhhNjAzLWMyNTMtNGQyNC05M2MzLWIzYzAwMzg2NjM5MCIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmRodDo5cWc4aDFzcm93aHNkc2VreHA4OTh5NTQxeGd0ZnhibXJtbmhoZ3NqeWhtdG04dGNvbndvIiwiaXNzdWFuY2VEYXRlIjoiMjAyNC0wNi0yOFQxMzoxOTo1OS45OTY2MzMrMDA6MDAiLCJleHBpcmF0aW9uRGF0ZSI6bnVsbCwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiJhbGljZSJ9fSwiaXNzIjoiZGlkOmRodDo5cWc4aDFzcm93aHNkc2VreHA4OTh5NTQxeGd0ZnhibXJtbmhoZ3NqeWhtdG04dGNvbndvIiwianRpIjoidXJuOnZjOnV1aWQ6OWQwOGE2MDMtYzI1My00ZDI0LTkzYzMtYjNjMDAzODY2MzkwIiwic3ViIjoiYWxpY2UiLCJuYmYiOjE3MTk1ODA3OTksImlhdCI6MTcxOTU4MDgwMH0.PJbb9EidggoqHL3IkfcglcTNzp_obBqbZjE0M4mL2XlecdLKNusZ3i4Hm0BtnzJ0ME7zYAvdIwg4shW4U884Bg
```


## Doctor Command

The `doctor` command is a feature to check the health of the cli. This command performs a series of diagnostic checks and reports the status of various functionalities. It is useful for ensuring that the system is functioning correctly and for troubleshooting potential issues.

### Usage

To use the `doctor` command, simply run:

```shell
#/bin/bash

web5 doctor
```

To apply for a specific check you can run:

```shell
#/bin/bash

web5 doctor --check cli-version
web5 doctor --check connectivity
web5 doctor --check env-vars
```
3 changes: 3 additions & 0 deletions crates/web5_cli/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ sudo mv /tmp/$FILENAME /usr/local/bin/web5

# Cleanup
rm /tmp/$FILENAME

# Running health check
/usr/local/bin/web5 doctor
218 changes: 218 additions & 0 deletions crates/web5_cli/src/doctor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
use std::collections::HashMap;

use crate::CheckType;

pub struct HealthCheckState {
cli_version: Option<Option<String>>,
dependencies: Option<HashMap<String, bool>>,
env_vars: Option<HashMap<String, bool>>,
connectivity: Option<bool>,
basic_functionality: Option<bool>,
}

impl HealthCheckState {
fn new() -> Self {
Self {
cli_version: None,
dependencies: None,
env_vars: None,
connectivity: None,
basic_functionality: None,
}
}
}

fn check_cli_version() -> Option<Option<String>> {
Some(Some(env!("CARGO_PKG_VERSION").to_string())) // This will return the binary version.
}

fn check_dependencies() -> Option<HashMap<String, bool>> {
// TODO : Implement this function
// DOUBT : Are we expecting to check system dependencies or dependencies in Cargo.toml ?
// If cargo dependencies then how exactly shall we check the versions ?
None
}

fn check_environment_variables() -> Option<HashMap<String, bool>> {
let mut env_vars = HashMap::new();
let vars = vec!["PORTABLE_DID"]; // Add env_vars that you want to include in health checkup

for var in vars {
let status = std::env::var(var).is_ok();
env_vars.insert(var.to_string(), status);
}

Some(env_vars)
}

async fn check_connectivity() -> Option<bool> {
let client = reqwest::Client::new();
Some(client.get("https://developer.tbd.website/projects/web5/").send().await.is_ok())
}

fn test_basic_functionality() -> Option<bool> {
// Supporting for actual binary
let web5_help = std::process::Command
::new("web5")
.arg("did")
.arg("create")
.arg("dht")
.output()
.is_ok();

// Supporting for cargo environment just for testing purposes.
let cargo_check = std::process::Command
::new("cargo")
.arg("run")
.arg("--")
.arg("did")
.arg("create")
.arg("dht")
.output()
.is_ok();

// If any one of the above commands is successful then return true.
Some(web5_help || cargo_check)
}

pub async fn run_health_checks(check: Option<CheckType>) -> HealthCheckState {
let mut state = HealthCheckState::new();

match check {
// Run specific checks
Some(CheckType::CliVersion) => {
state.cli_version = check_cli_version();
}
Some(CheckType::Dependencies) => {
state.dependencies = check_dependencies();
}
Some(CheckType::EnvVars) => {
state.env_vars = check_environment_variables();
}
Some(CheckType::Connectivity) => {
state.connectivity = check_connectivity().await;
}
Some(CheckType::BasicFunctionality) => {
state.basic_functionality = test_basic_functionality();
}
None => {
// Run all checks
state.cli_version = check_cli_version();
state.dependencies = check_dependencies();
state.env_vars = check_environment_variables();
state.connectivity = check_connectivity().await;
state.basic_functionality = test_basic_functionality();
}
}

state
}

use colored::*; // Add this line at the top of your file

pub fn print_health_check_results(state: &HealthCheckState) {
println!("{}", "Running Health Check for web5 CLI...".bold().blue());

// Handle CLI version
if let Some(cli_version) = &state.cli_version {
match cli_version {
Some(version) => println!("{} {}", "✔ CLI Version:".green(), version),
None =>
println!(
"{} {}",
"✖ CLI Version check failed.".red(),
"Please ensure the CLI is installed correctly.".yellow()
),
}
}

// Handle dependencies
if let Some(dependencies) = &state.dependencies {
for (dep, status) in dependencies {
println!(
"{} {}: {}",
if *status {
"✔".green()
} else {
"✖".red()
},
"Dependency".bold(),
dep
);
if !status {
println!(
"{} {}",
"Remediation:".yellow(),
format!("Please install or update the dependency: {}", dep).yellow()
);
}
}
}

// Handle environment variables
if let Some(env_vars) = &state.env_vars {
for (var, status) in env_vars {
println!(
"{} : {}",
if *status {
"✔ Environment Variable :".green()
} else {
"✖ Missing Environment Variable :".red()
},
if *status {
var.green()
} else {
var.red()
}
);
if !status {
println!("{}", format!("Please set the environment variable: {}", var).yellow());
// Example code to set the environment variable
println!("{}", format!("export {}=your_value", var).bright_yellow());
}
}
}

// Handle connectivity
if let Some(connectivity) = state.connectivity {
println!(
"{} {}",
if connectivity {
"✔ Connectivity:".green()
} else {
"✖ Connectivity:".red()
},
if connectivity {
"OK".green()
} else {
"FAILED".red()
}
);
if !connectivity {
println!("{}", "Please check your internet connection and try again.".yellow());
}
}

// Handle basic functionality
if let Some(basic_functionality) = state.basic_functionality {
println!(
"{} {}",
if basic_functionality {
"✔ Basic CLI Functionality:".green()
} else {
"✖ Basic CLI Functionality:".red()
},
if basic_functionality {
"OK".green()
} else {
"FAILED".red()
}
);
if !basic_functionality {
println!(
"{}",
"Might be a bug or your CLI have not been setup correctly. Please report on https://github.com/TBD54566975/web5-rs/issues ".yellow()
);
}
}
}
21 changes: 20 additions & 1 deletion crates/web5_cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
mod dids;
mod doctor;
mod test;
mod utils;
mod vcs;

use clap::{Parser, Subcommand};
use clap::{ Parser, Subcommand, ValueEnum };
use doctor::{ print_health_check_results, run_health_checks };

#[derive(Parser, Debug)]
#[command(
Expand All @@ -15,6 +17,15 @@ struct Cli {
command: Commands,
}

#[derive(Debug, Clone, ValueEnum)]
enum CheckType {
CliVersion,
Dependencies,
EnvVars,
Connectivity,
BasicFunctionality,
}

#[derive(Subcommand, Debug)]
enum Commands {
Did {
Expand All @@ -25,6 +36,10 @@ enum Commands {
#[command(subcommand)]
vc_command: vcs::Commands,
},
Doctor {
#[arg(long, value_enum)]
check: Option<CheckType>, // Optional argument for individual checks
},
}

#[tokio::main]
Expand All @@ -34,5 +49,9 @@ async fn main() {
match cli.command {
Commands::Did { did_command } => did_command.command().await,
Commands::Vc { vc_command } => vc_command.command().await,
Commands::Doctor { check } => {
let state = run_health_checks(check).await;
print_health_check_results(&state);
}
}
}
Loading