From 47304cc68d85c16015f9f414fedf308ad26400c7 Mon Sep 17 00:00:00 2001 From: houseme Date: Tue, 1 Sep 2026 17:08:46 +0800 Subject: [PATCH] feat(build): allow overriding release version (#6998) Allow release builds to set RUSTFS_BUILD_VERSION at compile time while keeping the existing tag, short commit, and package-version fallback when the variable is unset or empty. Co-authored-by: heihutu --- rustfs/build.rs | 11 +++++++ rustfs/src/admin/console.rs | 12 ++------ rustfs/src/config/cli.rs | 29 +++++++++++-------- rustfs/src/version.rs | 57 +++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 22 deletions(-) diff --git a/rustfs/build.rs b/rustfs/build.rs index 26e301fef..a302de37d 100644 --- a/rustfs/build.rs +++ b/rustfs/build.rs @@ -13,6 +13,17 @@ // limitations under the License. fn main() -> shadow_rs::SdResult<()> { + println!("cargo:rerun-if-env-changed=RUSTFS_BUILD_VERSION"); + if let Ok(version) = std::env::var("RUSTFS_BUILD_VERSION") + && !version.is_empty() + { + assert!( + !version.contains(['\n', '\r']), + "RUSTFS_BUILD_VERSION must be a single-line version string" + ); + println!("cargo:rustc-env=RUSTFS_BUILD_VERSION={version}"); + } + shadow_rs::ShadowBuilder::builder().build()?; Ok(()) } diff --git a/rustfs/src/admin/console.rs b/rustfs/src/admin/console.rs index 9523a9e59..d50fedc48 100644 --- a/rustfs/src/admin/console.rs +++ b/rustfs/src/admin/console.rs @@ -26,7 +26,7 @@ use crate::server::{ HeaderMapCarrier, HealthProbe, LICENSE, RUSTFS_ADMIN_PREFIX, RequestContextLayer, VERSION, build_health_response_parts, collect_probe_readiness, }; -use crate::version::build; +use crate::version::{self, build}; use axum::{ Extension, Json, Router, body::Body, @@ -262,15 +262,7 @@ static CONSOLE_CONFIG: OnceLock = OnceLock::new(); #[allow(clippy::const_is_empty)] pub(crate) fn init_console_cfg(local_ip: IpAddr, port: u16) { CONSOLE_CONFIG.get_or_init(|| { - let ver = { - if !build::TAG.is_empty() { - build::TAG.to_string() - } else if !build::SHORT_COMMIT.is_empty() { - format!("@{}", build::SHORT_COMMIT) - } else { - build::PKG_VERSION.to_string() - } - }; + let ver = version::DISPLAY_VERSION.to_string(); Config::new(local_ip, port, ver.as_str(), build::COMMIT_DATE_3339) }); diff --git a/rustfs/src/config/cli.rs b/rustfs/src/config/cli.rs index d6547218b..eec9f64b0 100644 --- a/rustfs/src/config/cli.rs +++ b/rustfs/src/config/cli.rs @@ -23,7 +23,7 @@ //! - `InfoType`: Information type enum //! - `CommandResult`: Result of parsing command line arguments -use crate::version::build; +use crate::version::{self, build}; use clap::builder::NonEmptyStringValueParser; use clap::{Args, Parser, Subcommand, ValueEnum}; use const_str::concat; @@ -31,16 +31,7 @@ use rustfs_config::{DEFAULT_ADDRESS, DEFAULT_CONSOLE_ADDRESS, DEFAULT_CONSOLE_EN use std::path::PathBuf; // build module is re-exported from crate::build -#[allow(clippy::const_is_empty)] -pub(super) const SHORT_VERSION: &str = { - if !build::TAG.is_empty() { - build::TAG - } else if !build::SHORT_COMMIT.is_empty() { - concat!("@", build::SHORT_COMMIT) - } else { - build::PKG_VERSION - } -}; +pub(super) const SHORT_VERSION: &str = version::DISPLAY_VERSION; pub(super) const LONG_VERSION: &str = concat!( concat!(SHORT_VERSION, "\n"), @@ -495,8 +486,9 @@ pub fn default_server_opts() -> ServerOpts { #[cfg(test)] mod tests { use super::{Cli, Commands, ConnectCommands, InspectCommands, preprocess_args_for_legacy}; - use clap::Parser; + use crate::version; use clap::error::ErrorKind; + use clap::{CommandFactory, Parser}; #[test] fn preprocess_help_command_displays_top_level_help() { @@ -511,6 +503,19 @@ mod tests { assert_eq!(err.kind(), ErrorKind::DisplayHelp); } + #[test] + fn version_flags_use_display_version() { + let command = Cli::command(); + + let short = command.render_version(); + assert!(short.contains(version::DISPLAY_VERSION)); + assert!(!short.contains("build time")); + + let long = command.render_long_version(); + assert!(long.starts_with(&format!("rustfs {}\n", version::DISPLAY_VERSION))); + assert!(long.contains("build time")); + } + #[test] fn inspect_bucket_meta_parses_repeated_drive_paths() { let cli = Cli::try_parse_from([ diff --git a/rustfs/src/version.rs b/rustfs/src/version.rs index 18de153fe..d94adedaf 100644 --- a/rustfs/src/version.rs +++ b/rustfs/src/version.rs @@ -12,16 +12,59 @@ // See the License for the specific language governing permissions and // limitations under the License. +use const_str::concat; use shadow_rs::shadow; use std::path::Path; use std::process::Command; shadow!(build); +const BUILD_VERSION_OVERRIDE: Option<&str> = option_env!("RUSTFS_BUILD_VERSION"); + +#[allow(clippy::const_is_empty)] +const DEFAULT_DISPLAY_VERSION: &str = { + if !build::TAG.is_empty() { + build::TAG + } else if !build::SHORT_COMMIT.is_empty() { + concat!("@", build::SHORT_COMMIT) + } else { + build::PKG_VERSION + } +}; + +#[allow(clippy::const_is_empty)] +pub const DISPLAY_VERSION: &str = { + if let Some(version) = BUILD_VERSION_OVERRIDE { + if !version.is_empty() { + version + } else { + DEFAULT_DISPLAY_VERSION + } + } else { + DEFAULT_DISPLAY_VERSION + } +}; + type VersionParseResult = Result<(u32, u32, u32, Option), Box>; +fn build_version_override() -> Option<&'static str> { + BUILD_VERSION_OVERRIDE.filter(|version| !version.is_empty()) +} + +fn version_ref(version: &str) -> String { + if version.starts_with("refs/tags/") || version.starts_with('@') { + version.to_string() + } else { + format!("refs/tags/{version}") + } +} + #[allow(clippy::const_is_empty)] pub fn get_version() -> String { + if let Some(version) = build_version_override() { + return version_ref(version); + } + // Get the latest tag if let Ok(latest_tag) = get_latest_tag() { // Check if current commit is newer than the latest tag @@ -262,6 +305,20 @@ mod tests { assert!(is_head_newer_than_tag_in(repo.path(), "1.2.3")); } + #[test] + fn build_version_override_is_used_for_current_version_when_set() { + if let Some(version) = build_version_override() { + assert_eq!(get_version(), version_ref(version)); + } + } + + #[test] + fn version_ref_keeps_existing_ref_prefixes() { + assert_eq!(version_ref("1.2.3"), "refs/tags/1.2.3"); + assert_eq!(version_ref("refs/tags/1.2.3"), "refs/tags/1.2.3"); + assert_eq!(version_ref("@abc123"), "@abc123"); + } + #[test] fn test_parse_version() { // Test standard version parsing