Skip to content

Commit

Permalink
feat: check for updates in background and print on next run
Browse files Browse the repository at this point in the history
feat: check for updates in background and print on next run

remove updates check from init, link and run

move functions around in main.rs
  • Loading branch information
alexng353 committed Feb 20, 2025
1 parent 8132487 commit 3a86854
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 43 deletions.
4 changes: 1 addition & 3 deletions src/commands/init.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fmt::Display;

use crate::util::{check_update::check_update_command, prompt::prompt_select};
use crate::util::prompt::prompt_select;

use super::{queries::user_projects::UserProjectsMeTeamsEdgesNode, *};

Expand All @@ -16,8 +16,6 @@ pub struct Args {
pub async fn command(args: Args, _json: bool) -> Result<()> {
let mut configs = Configs::new()?;

check_update_command(&mut configs).await?;

let client = GQLClient::new_authorized(&configs)?;

let vars = queries::user_projects::Variables {};
Expand Down
7 changes: 1 addition & 6 deletions src/commands/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ use std::fmt::Display;

use crate::{
errors::RailwayError,
util::{
check_update::check_update_command,
prompt::{fake_select, prompt_options, prompt_options_skippable},
},
util::prompt::{fake_select, prompt_options, prompt_options_skippable},
};

use super::{
Expand Down Expand Up @@ -42,8 +39,6 @@ pub struct Args {
pub async fn command(args: Args, _json: bool) -> Result<()> {
let mut configs = Configs::new()?;

check_update_command(&mut configs).await?;

let client = GQLClient::new_authorized(&configs)?;
let me = post_graphql::<queries::UserProjects, _>(
&client,
Expand Down
9 changes: 2 additions & 7 deletions src/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ use crate::{
variables::get_service_variables,
},
errors::RailwayError,
util::{
check_update::check_update_command,
prompt::{prompt_select, PromptService},
},
util::prompt::{prompt_select, PromptService},
};

use super::{queries::project::ProjectProject, *};
Expand Down Expand Up @@ -77,9 +74,7 @@ async fn get_service(

pub async fn command(args: Args, _json: bool) -> Result<()> {
// only needs to be mutable for the update check
let mut configs = Configs::new()?;
check_update_command(&mut configs).await?;
let configs = configs; // so we make it immutable again
let configs = Configs::new()?;

let client = GQLClient::new_authorized(&configs)?;
let linked_project = configs.get_linked_project().await?;
Expand Down
4 changes: 4 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub struct RailwayConfig {
pub projects: BTreeMap<String, LinkedProject>,
pub user: RailwayUser,
pub last_update_check: Option<DateTime<Utc>>,
pub new_version_available: Option<String>,
}

#[derive(Debug)]
Expand Down Expand Up @@ -90,6 +91,7 @@ impl Configs {
projects: BTreeMap::new(),
user: RailwayUser { token: None },
last_update_check: None,
new_version_available: None,
}
});

Expand All @@ -107,6 +109,7 @@ impl Configs {
projects: BTreeMap::new(),
user: RailwayUser { token: None },
last_update_check: None,
new_version_available: None,
},
})
}
Expand All @@ -116,6 +119,7 @@ impl Configs {
projects: BTreeMap::new(),
user: RailwayUser { token: None },
last_update_check: None,
new_version_available: None,
};
Ok(())
}
Expand Down
119 changes: 97 additions & 22 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use anyhow::Result;
use clap::{Parser, Subcommand};
use clap::{error::ErrorKind, Parser, Subcommand};

mod commands;
use commands::*;
use util::check_update::check_update_command;
use is_terminal::IsTerminal;

mod client;
mod config;
Expand Down Expand Up @@ -63,36 +63,111 @@ commands_enum!(
check_updates
);

fn spawn_update_task() -> tokio::task::JoinHandle<Result<(), anyhow::Error>> {
tokio::spawn(async {
if !std::io::stdout().is_terminal() {
return Ok::<(), anyhow::Error>(());
}

let mut configs = Configs::new()?;
let result = configs.check_update(false).await;
if let Ok(Some(latest_version)) = result {
configs.root_config.new_version_available = Some(latest_version);
}
configs.write()?;
Ok::<(), anyhow::Error>(())
})
}

async fn handle_update_task(handle: tokio::task::JoinHandle<Result<(), anyhow::Error>>) {
match handle.await {
Ok(Ok(_)) => {} // Task completed successfully
Ok(Err(e)) => {
if !std::io::stdout().is_terminal() {
eprintln!("Failed to check for updates (not fatal)");
eprintln!("{}", e);
}
}
Err(e) => {
eprintln!("Check Updates: Task panicked or failed to execute.");
eprintln!("{}", e);
}
}
}

#[tokio::main]
async fn main() -> Result<()> {
// intercept the args
{
let args: Vec<String> = std::env::args().collect();
let flags = ["--version", "-V", "-h", "--help", "help"];
let check_version = args.into_iter().any(|arg| flags.contains(&arg.as_str()));

if check_version {
let mut configs = Configs::new()?;
check_update_command(&mut configs).await?;
if std::io::stdout().is_terminal() {
let mut configs = Configs::new()?;
if let Some(new_version_available) = configs.root_config.new_version_available {
println!(
"{} v{} visit {} for more info",
"New version available:".green().bold(),
new_version_available.yellow(),
"https://docs.railway.com/guides/cli".purple(),
);
configs.root_config.new_version_available = None;
configs.write()?;
}
}

let cli = Args::parse();
let check_updates_handle = spawn_update_task();

// Trace from where Args::parse() bubbles an error to where it gets caught
// and handled.
//
// https://github.com/clap-rs/clap/blob/cb2352f84a7663f32a89e70f01ad24446d5fa1e2/clap_builder/src/derive.rs#L30-L42
// https://github.com/clap-rs/clap/blob/cb2352f84a7663f32a89e70f01ad24446d5fa1e2/clap_builder/src/error/mod.rs#L233-L237
//
// This code tells us what exit code to use:
// https://github.com/clap-rs/clap/blob/cb2352f84a7663f32a89e70f01ad24446d5fa1e2/clap_builder/src/error/mod.rs#L221-L227
//
// https://github.com/clap-rs/clap/blob/cb2352f84a7663f32a89e70f01ad24446d5fa1e2/clap_builder/src/error/mod.rs#L206-L208
//
// This code tells us what stream to print the error to:
// https://github.com/clap-rs/clap/blob/cb2352f84a7663f32a89e70f01ad24446d5fa1e2/clap_builder/src/error/mod.rs#L210-L215
//
// pub(crate) fn stream(&self) -> Stream {
// match self.kind() {
// ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => Stream::Stdout,
// _ => Stream::Stderr,
// }
// }

match Commands::exec(cli).await {
Ok(_) => {}
let cli = match Args::try_parse() {
Ok(args) => args,
// Cases where Clap usually exits the process with and prints to stdout.
Err(e) if e.kind() == ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand => {
eprintln!("{}", e);
handle_update_task(check_updates_handle).await;
std::process::exit(2); // Exit 2 (default)
}
// Clap's source code specifically says that these errors should be
// printed to stdout and exit with a status of 0.
Err(e) if e.kind() == ErrorKind::DisplayHelp || e.kind() == ErrorKind::DisplayVersion => {
println!("{}", e);
handle_update_task(check_updates_handle).await;
std::process::exit(0); // Exit 0 (because of error kind)
}
Err(e) => {
// If the user cancels the operation, we want to exit successfully
// This can happen if Ctrl+C is pressed during a prompt
if e.root_cause().to_string() == inquire::InquireError::OperationInterrupted.to_string()
{
return Ok(());
}
eprintln!("Error parsing arguments: {}", e);
handle_update_task(check_updates_handle).await;
std::process::exit(2); // Exit 2 (default)
}
};

eprintln!("{:?}", e);
std::process::exit(1);
let exec_result = Commands::exec(cli).await;

if let Err(e) = exec_result {
if e.root_cause().to_string() == inquire::InquireError::OperationInterrupted.to_string() {
return Ok(()); // Exit gracefully if interrupted
}
eprintln!("{:?}", e);
handle_update_task(check_updates_handle).await;
std::process::exit(1);
}

handle_update_task(check_updates_handle).await;

Ok(())
}
5 changes: 0 additions & 5 deletions src/util/check_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,3 @@ pub async fn check_update(configs: &mut crate::Configs, force: bool) -> anyhow::
Ok(true)
}
}

pub async fn check_update_command(configs: &mut crate::Configs) -> anyhow::Result<()> {
check_update(configs, false).await?;
Ok(())
}

0 comments on commit 3a86854

Please sign in to comment.