diff --git a/build.rs b/build.rs index e65e19a84..ec593eb71 100644 --- a/build.rs +++ b/build.rs @@ -5,8 +5,41 @@ fn main() { println!("cargo:rerun-if-changed=src/gql/subscriptions/strings"); println!("cargo:rerun-if-changed=src/gql/schema.json"); + // Rebuild when git HEAD changes + println!("cargo:rerun-if-changed=.git/HEAD"); + // Expose the compile-time target triple so the self-updater fetches the // correct release asset (respects ABI: gnu vs musl, msvc vs gnu, etc.). let target = std::env::var("TARGET").unwrap(); println!("cargo:rustc-env=BUILD_TARGET={target}"); + + // Expose git commit hash + let git_sha = std::process::Command::new("git") + .args(["rev-parse", "--short", "HEAD"]) + .output() + .ok() + .and_then(|output| String::from_utf8(output.stdout).ok()) + .map(|s| s.trim().to_string()) + .unwrap_or_else(|| "unknown".to_string()); + println!("cargo:rustc-env=GIT_SHA={}", git_sha); + + // Expose build date (ISO 8601 format in UTC) + let build_date = std::process::Command::new("date") + .args(["-u", "+%Y-%m-%dT%H:%M:%SZ"]) + .output() + .ok() + .and_then(|output| String::from_utf8(output.stdout).ok()) + .map(|s| s.trim().to_string()) + .unwrap_or_else(|| "unknown".to_string()); + println!("cargo:rustc-env=BUILD_DATE={}", build_date); + + // Expose rustc version + let rustc_version = std::process::Command::new("rustc") + .args(["--version"]) + .output() + .ok() + .and_then(|output| String::from_utf8(output.stdout).ok()) + .map(|s| s.trim().to_string()) + .unwrap_or_else(|| "unknown".to_string()); + println!("cargo:rustc-env=RUSTC_VERSION={}", rustc_version); } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 188e50802..9104175c2 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -46,6 +46,7 @@ pub mod unlink; pub mod up; pub mod upgrade; pub mod variable; +pub mod version; pub mod volume; pub mod whoami; diff --git a/src/commands/version.rs b/src/commands/version.rs new file mode 100644 index 000000000..1198cf088 --- /dev/null +++ b/src/commands/version.rs @@ -0,0 +1,78 @@ +use serde::Serialize; + +use super::*; +use crate::util::check_update::check_update; + +/// Display version information +#[derive(Parser)] +pub struct Args { + /// Output in JSON format + #[clap(long)] + json: bool, +} + +#[derive(Serialize)] +struct VersionJson { + version: String, + git_sha: String, + build_date: String, + rust_version: String, + target: String, + update_available: Option, +} + +pub async fn command(args: Args) -> Result<()> { + let version = env!("CARGO_PKG_VERSION").to_string(); + let git_sha = env!("GIT_SHA").to_string(); + let build_date = env!("BUILD_DATE").to_string(); + let target = env!("BUILD_TARGET").to_string(); + let rust_version = env!("RUSTC_VERSION").to_string(); + + // Check for updates (non-blocking, don't fail if check fails) + let update_available = check_update(false).await.ok().flatten(); + + if args.json { + let output = VersionJson { + version: version.clone(), + git_sha: git_sha.clone(), + build_date: build_date.clone(), + rust_version: rust_version.clone(), + target: target.clone(), + update_available: update_available.clone(), + }; + println!("{}", serde_json::to_string_pretty(&output)?); + return Ok(()); + } + + // Human-readable output + println!( + "{} {}", + "Railway CLI".purple().bold(), + version.green().bold() + ); + println!(); + println!("{}", "Build Info:".bold()); + println!(" Version: {}", version); + println!(" Commit: {}", git_sha.dimmed()); + println!(" Build Date: {}", build_date); + println!(" Rust: {}", rust_version); + println!(" Target: {}", target); + println!(); + println!("{}", "Update Status:".bold()); + match update_available { + Some(new_version) => { + println!( + " {} New version available: {} -> {}", + "*".yellow(), + version.red(), + new_version.green() + ); + println!(" Run {} to upgrade", "`railway upgrade`".cyan()); + } + None => { + println!(" {} You are on the latest version", "*".green()); + } + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 96f9a8fdf..478132652 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,6 +62,7 @@ commands!( unlink, up, upgrade, + version, variable(variables, vars, var), whoami, volume,