From c80dd3bca9cf129215c0964442602166a3adc2e8 Mon Sep 17 00:00:00 2001 From: omri Date: Thu, 29 May 2025 10:12:55 +0000 Subject: [PATCH 1/6] Add versoning to election event. the version is being added at export level from .env file and is being checked at import. if system is in dev version not need to compare. --- .../services/export/export_election_event.rs | 3 ++ .../services/import/import_election_event.rs | 42 ++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/windmill/src/services/export/export_election_event.rs b/packages/windmill/src/services/export/export_election_event.rs index b04af050f49..d8945ddf7df 100644 --- a/packages/windmill/src/services/export/export_election_event.rs +++ b/packages/windmill/src/services/export/export_election_event.rs @@ -136,6 +136,8 @@ pub async fn read_export_data( vec![] }; + let version = std::env::var("APP_VERSION").unwrap_or_else(|_| "dev".to_string()); + Ok(ImportElectionEventSchema { tenant_id: Uuid::parse_str(&tenant_id)?, keycloak_event_realm: Some(realm), @@ -149,6 +151,7 @@ pub async fn read_export_data( reports: export_reports, keys_ceremonies: Some(export_keys_ceremonies), applications: Some(export_applications), + version, }) } diff --git a/packages/windmill/src/services/import/import_election_event.rs b/packages/windmill/src/services/import/import_election_event.rs index 1ebdc35c2db..bfe7af305df 100644 --- a/packages/windmill/src/services/import/import_election_event.rs +++ b/packages/windmill/src/services/import/import_election_event.rs @@ -102,6 +102,7 @@ pub struct ImportElectionEventSchema { pub reports: Vec, pub keys_ceremonies: Option>, pub applications: Option>, + pub version: String, } #[instrument(err)] @@ -398,6 +399,42 @@ pub async fn decrypt_document( Ok(temp_file_path) } +fn extract_major(version: &str) -> Option { + version.split('.') + .next() + .and_then(|v| v.parse::().ok()) +} + +fn check_version_compatibility(imported_version: &str, current_version: &str) -> Result<()> { + + + info!("Checking version compatibility - Current: {}, Imported: {}", current_version, imported_version); + + // If current version is "dev", allow any import + if current_version == "dev" { + info!("Current version is 'dev', allowing import"); + return Ok(()); + } + + if imported_version == "dev" { + info!("Imported version is 'dev' while system is not in dev mode, rejecting import"); + return Err(anyhow!("Imported version is 'dev', which is not compatible with current version {}. Please use a different version.", current_version)); + } + + let current_major_parsed = extract_major(¤t_version).ok_or_else(|| anyhow!("Could not parse current version"))?; + let imported_major_parsed = extract_major(imported_version).ok_or_else(|| anyhow!("Could not parse imported version"))?; + + + if current_major_parsed < imported_major_parsed { + return Err(anyhow!( + "Version mismatch: Imported version {} is not compatible with current version {}. Please upgrade your system.", + imported_version, + current_version + )); + } + Ok(()) +} + #[instrument(err, skip_all)] pub async fn get_election_event_schema( data_str: &str, @@ -405,6 +442,8 @@ pub async fn get_election_event_schema( tenant_id: String, ) -> Result<(ImportElectionEventSchema, HashMap)> { let original_data: ImportElectionEventSchema = deserialize_str(data_str)?; + let current_version = std::env::var("APP_VERSION").unwrap_or_else(|_| "dev".to_string()); + check_version_compatibility(&original_data.version, ¤t_version).with_context(|| anyhow!("Version mismatch: Imported version {} is not compatible with current version {}. Please upgrade your system.", original_data.version, current_version))?; replace_ids(data_str, &original_data, id, tenant_id.clone()) } @@ -424,7 +463,8 @@ pub async fn process_election_event_file( tenant_id.clone(), ) .await - .with_context(|| format!("Error getting document for election event ID {election_event_id} and tenant ID {tenant_id}"))?; + .map_err(|err| anyhow!("Error getting document for election event ID {election_event_id} and tenant ID {tenant_id}: {err}"))?; + let election_ids: Vec = data .elections From 62d280f8737b89a5592ffa27b689585d20549f2c Mon Sep 17 00:00:00 2001 From: omri Date: Thu, 29 May 2025 10:13:49 +0000 Subject: [PATCH 2/6] cargo fmt --- .../services/import/import_election_event.rs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/windmill/src/services/import/import_election_event.rs b/packages/windmill/src/services/import/import_election_event.rs index bfe7af305df..a32348472b6 100644 --- a/packages/windmill/src/services/import/import_election_event.rs +++ b/packages/windmill/src/services/import/import_election_event.rs @@ -400,16 +400,18 @@ pub async fn decrypt_document( } fn extract_major(version: &str) -> Option { - version.split('.') + version + .split('.') .next() .and_then(|v| v.parse::().ok()) } fn check_version_compatibility(imported_version: &str, current_version: &str) -> Result<()> { - - - info!("Checking version compatibility - Current: {}, Imported: {}", current_version, imported_version); - + info!( + "Checking version compatibility - Current: {}, Imported: {}", + current_version, imported_version + ); + // If current version is "dev", allow any import if current_version == "dev" { info!("Current version is 'dev', allowing import"); @@ -421,9 +423,10 @@ fn check_version_compatibility(imported_version: &str, current_version: &str) -> return Err(anyhow!("Imported version is 'dev', which is not compatible with current version {}. Please use a different version.", current_version)); } - let current_major_parsed = extract_major(¤t_version).ok_or_else(|| anyhow!("Could not parse current version"))?; - let imported_major_parsed = extract_major(imported_version).ok_or_else(|| anyhow!("Could not parse imported version"))?; - + let current_major_parsed = extract_major(¤t_version) + .ok_or_else(|| anyhow!("Could not parse current version"))?; + let imported_major_parsed = extract_major(imported_version) + .ok_or_else(|| anyhow!("Could not parse imported version"))?; if current_major_parsed < imported_major_parsed { return Err(anyhow!( @@ -465,7 +468,6 @@ pub async fn process_election_event_file( .await .map_err(|err| anyhow!("Error getting document for election event ID {election_event_id} and tenant ID {tenant_id}: {err}"))?; - let election_ids: Vec = data .elections .clone() From 97fedcd74da4680c8dd79b5843923b85a9b348fd Mon Sep 17 00:00:00 2001 From: xalsina-sequent Date: Fri, 5 Dec 2025 11:51:10 +0000 Subject: [PATCH 3/6] Added formatting to exported json. Fixed version check. --- .../services/export/export_election_event.rs | 2 +- .../services/import/import_election_event.rs | 24 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/windmill/src/services/export/export_election_event.rs b/packages/windmill/src/services/export/export_election_event.rs index 40174c71f60..3f6d60c6eb7 100644 --- a/packages/windmill/src/services/export/export_election_event.rs +++ b/packages/windmill/src/services/export/export_election_event.rs @@ -169,7 +169,7 @@ pub async fn generate_encrypted_zip( pub async fn write_export_document(data: ImportElectionEventSchema) -> Result { // Serialize the data into JSON string - let data_str = serde_json::to_string(&data)?; + let data_str = serde_json::to_string_pretty(&data)?; let data_bytes = data_str.into_bytes(); // Create and write the data into a temporary file diff --git a/packages/windmill/src/services/import/import_election_event.rs b/packages/windmill/src/services/import/import_election_event.rs index 9476a13a30f..649c8cd4f65 100644 --- a/packages/windmill/src/services/import/import_election_event.rs +++ b/packages/windmill/src/services/import/import_election_event.rs @@ -103,9 +103,15 @@ pub struct ImportElectionEventSchema { pub reports: Vec, pub keys_ceremonies: Option>, pub applications: Option>, + #[serde(default = "default_version")] pub version: String, } +// Set the default version of an imported election event to be compatible with version 9, which is the first version to include this feature. +fn default_version() -> String { + "9.0.0".to_string() +} + #[instrument(err)] pub async fn upsert_b3_and_elog( hasura_transaction: &Transaction<'_>, @@ -462,11 +468,17 @@ pub async fn decrypt_document( Ok(temp_file_path) } -fn extract_major(version: &str) -> Option { - version - .split('.') - .next() - .and_then(|v| v.parse::().ok()) +fn extract_major(input: &str) -> Option { + // Trim optional 'v' or 'V' prefix + let trimmed = input.trim_start_matches(|c| c == 'v' || c == 'V'); + + // We take characters from the start as long as they are digits. + // This stops at the first dot '.', hyphen '-', or any non-digit. + let major_str: String = trimmed.chars().take_while(|c| c.is_ascii_digit()).collect(); + + // Parse the result into a u64 + // If the string was empty (e.g., input was "invalid"), this returns None. + major_str.parse::().ok() } fn check_version_compatibility(imported_version: &str, current_version: &str) -> Result<()> { @@ -509,7 +521,7 @@ pub async fn get_election_event_schema( ) -> Result<(ImportElectionEventSchema, HashMap)> { let original_data: ImportElectionEventSchema = deserialize_str(data_str)?; let current_version = std::env::var("APP_VERSION").unwrap_or_else(|_| "dev".to_string()); - check_version_compatibility(&original_data.version, ¤t_version).with_context(|| anyhow!("Version mismatch: Imported version {} is not compatible with current version {}. Please upgrade your system.", original_data.version, current_version))?; + check_version_compatibility(&original_data.version, ¤t_version)?; replace_ids(data_str, &original_data, id, tenant_id.clone()) } From 1c91616b9aebb1cc1be3726f519dd356a68c4290 Mon Sep 17 00:00:00 2001 From: xalsina-sequent Date: Tue, 9 Dec 2025 11:59:08 +0000 Subject: [PATCH 4/6] Added version as a sequent-core util --- packages/sequent-core/Cargo.toml | 1 + packages/sequent-core/src/util/mod.rs | 2 + packages/sequent-core/src/util/version.rs | 161 ++++++++++++++++++ packages/windmill/Cargo.toml | 2 +- .../services/export/export_election_event.rs | 3 +- .../services/import/import_election_event.rs | 48 +----- .../src/services/reports/report_variables.rs | 5 +- 7 files changed, 172 insertions(+), 50 deletions(-) create mode 100644 packages/sequent-core/src/util/version.rs diff --git a/packages/sequent-core/Cargo.toml b/packages/sequent-core/Cargo.toml index 50a3baa1db5..6ac5c7ade4d 100644 --- a/packages/sequent-core/Cargo.toml +++ b/packages/sequent-core/Cargo.toml @@ -143,6 +143,7 @@ probe = ["dep:tokio", "dep:warp"] lambda_inplace = [] lambda_openwhisk = [] lambda_aws_lambda = [] +version = ["dep:tracing"] sqlite = ["dep:tokio", "dep:rusqlite"] diff --git a/packages/sequent-core/src/util/mod.rs b/packages/sequent-core/src/util/mod.rs index 43dcbcd9fa4..15f29611257 100644 --- a/packages/sequent-core/src/util/mod.rs +++ b/packages/sequent-core/src/util/mod.rs @@ -7,6 +7,8 @@ pub mod date_time; pub mod integrity_check; pub mod mime; pub mod normalize_vote; +#[cfg(feature = "version")] +pub mod version; #[cfg(feature = "reports")] pub mod temp_path; diff --git a/packages/sequent-core/src/util/version.rs b/packages/sequent-core/src/util/version.rs new file mode 100644 index 00000000000..1bf7e7d681f --- /dev/null +++ b/packages/sequent-core/src/util/version.rs @@ -0,0 +1,161 @@ +// SPDX-FileCopyrightText: 2025 Sequent Tech Inc +// +// SPDX-License-Identifier: AGPL-3.0-only + +use tracing::{info, instrument}; +use anyhow::{Result, anyhow}; + +pub const DEV_APP_VERSION: &str = "dev"; +pub const ENV_VAR_APP_VERSION: &str = "APP_VERSION"; +pub const ENV_VAR_APP_HASH: &str = "APP_HASH"; + +pub fn check_version_compatibility(imported_version: &str, current_version: &str) -> Result<()> { + info!( + "Checking version compatibility - Current: {}, Imported: {}", + current_version, imported_version + ); + + // If current version is DEV_APP_VERSION, allow any import + if current_version == DEV_APP_VERSION { + info!("Current version is 'dev', allowing import"); + return Ok(()); + } + + if imported_version == DEV_APP_VERSION { + info!("Imported version is 'dev' while system is not in dev mode, rejecting import"); + return Err(anyhow!("Imported version is 'dev', which is not compatible with current version {}. Please use a different version.", current_version)); + } + + let current_major_parsed = extract_major(¤t_version) + .ok_or_else(|| anyhow!("Could not parse current version"))?; + let imported_major_parsed = extract_major(imported_version) + .ok_or_else(|| anyhow!("Could not parse imported version"))?; + + if current_major_parsed < imported_major_parsed { + return Err(anyhow!( + "Version mismatch: Imported version {} is not compatible with current version {}. Please upgrade your system.", + imported_version, + current_version + )); + } + Ok(()) +} + +fn extract_major(input: &str) -> Option { + // Trim optional 'v' or 'V' prefix + let trimmed = input.trim_start_matches(|c| c == 'v' || c == 'V'); + + // We take characters from the start as long as they are digits. + // This stops at the first dot '.', hyphen '-', or any non-digit. + let major_str: String = trimmed.chars().take_while(|c| c.is_ascii_digit()).collect(); + + // Parse the result into a u64 + // If the string was empty (e.g., input was "invalid"), this returns None. + major_str.parse::().ok() +} + +#[cfg(test)] +mod tests { + use super::*; + + // ========================================== + // Public API Tests: check_version_compatibility + // ========================================== + + #[test] + fn test_current_version_is_dev() { + // If current system is DEV_APP_VERSION, it should accept anything + assert!(check_version_compatibility("1.0.0", DEV_APP_VERSION).is_ok()); + assert!(check_version_compatibility("99.99.99", DEV_APP_VERSION).is_ok()); + assert!(check_version_compatibility(DEV_APP_VERSION, DEV_APP_VERSION).is_ok()); + } + + #[test] + fn test_imported_version_is_dev_rejected() { + // If importing DEV_APP_VERSION into a non-dev system, it must fail + let result = check_version_compatibility(DEV_APP_VERSION, "1.0.0"); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "Imported version is 'dev', which is not compatible with current version 1.0.0. Please use a different version." + ); + } + + #[test] + fn test_exact_match_versions() { + assert!(check_version_compatibility("1.0.0", "1.0.0").is_ok()); + assert!(check_version_compatibility("2.5.1", "2.5.1").is_ok()); + } + + #[test] + fn test_backward_compatibility() { + // Importing an OLDER version into a NEWER system should be OK + // Imported: 1, Current: 2 + assert!(check_version_compatibility("1.0.0", "2.0.0").is_ok()); + + // Imported: 10, Current: 11 + assert!(check_version_compatibility("10.5.5", "11.0.0").is_ok()); + } + + #[test] + fn test_forward_compatibility_rejection() { + // Importing a NEWER version into an OLDER system should FAIL + // Imported: 2, Current: 1 + let result = check_version_compatibility("2.0.0", "1.0.0"); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("not compatible")); + } + + #[test] + fn test_parsing_failures() { + // Invalid current version + let res_current = check_version_compatibility("1.0.0", "invalid_ver"); + assert!(res_current.is_err()); + assert!(res_current.unwrap_err().to_string().contains("Could not parse current version")); + + // Invalid imported version + let res_imported = check_version_compatibility("invalid_ver", "1.0.0"); + assert!(res_imported.is_err()); + assert!(res_imported.unwrap_err().to_string().contains("Could not parse imported version")); + } + + #[test] + fn test_version_prefixes() { + // Handling 'v' or 'V' prefixes + // Imported v1 (1) into Current 1 -> OK + assert!(check_version_compatibility("v1.0.0", "1.0.0").is_ok()); + + // Imported V2 (2) into Current v1 (1) -> Error + assert!(check_version_compatibility("V2.0.0", "v1.0.0").is_err()); + } + + // ========================================== + // Internal Helper Tests: extract_major + // ========================================== + + #[test] + fn test_extract_major_logic() { + // Standard semver + assert_eq!(extract_major("1.2.3"), Some(1)); + assert_eq!(extract_major("10.0.0"), Some(10)); + assert_eq!(extract_major("0.5.9"), Some(0)); + + // With prefixes + assert_eq!(extract_major("v1.2.3"), Some(1)); + assert_eq!(extract_major("V2.0.0"), Some(2)); + + // With suffixes (alpha, beta, rc) + assert_eq!(extract_major("1.0.0-alpha"), Some(1)); + assert_eq!(extract_major("3.0.0-rc1"), Some(3)); + assert_eq!(extract_major("v4-beta"), Some(4)); + + // Edge cases + assert_eq!(extract_major("2"), Some(2)); // Just a number + assert_eq!(extract_major("not_a_number"), None); + assert_eq!(extract_major(""), None); + assert_eq!(extract_major("v"), None); + + // Ensure it stops at non-digits + assert_eq!(extract_major("5startswithnumber"), Some(5)); + } +} \ No newline at end of file diff --git a/packages/windmill/Cargo.toml b/packages/windmill/Cargo.toml index fedd2ae3234..0aea7439046 100644 --- a/packages/windmill/Cargo.toml +++ b/packages/windmill/Cargo.toml @@ -80,7 +80,7 @@ immudb-rs = { path="../immudb-rs" } b3 = { path="../b3", features = ["client"] } electoral-log = { path="../electoral-log" } strand = { path="../strand" } -sequent-core = { path="../sequent-core", features = ["reports", "s3", "keycloak", "log", "probe", "areas", "sqlite"] } +sequent-core = { path="../sequent-core", features = ["reports", "s3", "keycloak", "log", "probe", "areas", "sqlite", "version"] } velvet = { path="../velvet" } # To connect to the Database diff --git a/packages/windmill/src/services/export/export_election_event.rs b/packages/windmill/src/services/export/export_election_event.rs index 3f6d60c6eb7..c376baeb298 100644 --- a/packages/windmill/src/services/export/export_election_event.rs +++ b/packages/windmill/src/services/export/export_election_event.rs @@ -32,6 +32,7 @@ use sequent_core::services::keycloak::KeycloakAdminClient; use sequent_core::services::s3; use sequent_core::types::hasura::core::Election; use sequent_core::types::hasura::core::KeysCeremony; +use sequent_core::util::version::{ENV_VAR_APP_VERSION, DEV_APP_VERSION}; use std::collections::HashMap; use std::env; use std::fs::File; @@ -136,7 +137,7 @@ pub async fn read_export_data( vec![] }; - let version = std::env::var("APP_VERSION").unwrap_or_else(|_| "dev".to_string()); + let version = std::env::var(ENV_VAR_APP_VERSION).unwrap_or_else(|_| DEV_APP_VERSION.to_string()); Ok(ImportElectionEventSchema { tenant_id: Uuid::parse_str(&tenant_id)?, diff --git a/packages/windmill/src/services/import/import_election_event.rs b/packages/windmill/src/services/import/import_election_event.rs index 649c8cd4f65..69960ad92b5 100644 --- a/packages/windmill/src/services/import/import_election_event.rs +++ b/packages/windmill/src/services/import/import_election_event.rs @@ -43,6 +43,7 @@ use sequent_core::types::hasura::core::Document; use sequent_core::types::hasura::core::KeysCeremony; use sequent_core::types::hasura::core::TasksExecution; use sequent_core::util::mime::{get_mime_types, matches_mime}; +use sequent_core::util::version::{ENV_VAR_APP_VERSION, DEV_APP_VERSION, check_version_compatibility}; use serde::{Deserialize, Serialize}; use serde_json::{json, Map, Value}; use std::collections::HashMap; @@ -468,51 +469,6 @@ pub async fn decrypt_document( Ok(temp_file_path) } -fn extract_major(input: &str) -> Option { - // Trim optional 'v' or 'V' prefix - let trimmed = input.trim_start_matches(|c| c == 'v' || c == 'V'); - - // We take characters from the start as long as they are digits. - // This stops at the first dot '.', hyphen '-', or any non-digit. - let major_str: String = trimmed.chars().take_while(|c| c.is_ascii_digit()).collect(); - - // Parse the result into a u64 - // If the string was empty (e.g., input was "invalid"), this returns None. - major_str.parse::().ok() -} - -fn check_version_compatibility(imported_version: &str, current_version: &str) -> Result<()> { - info!( - "Checking version compatibility - Current: {}, Imported: {}", - current_version, imported_version - ); - - // If current version is "dev", allow any import - if current_version == "dev" { - info!("Current version is 'dev', allowing import"); - return Ok(()); - } - - if imported_version == "dev" { - info!("Imported version is 'dev' while system is not in dev mode, rejecting import"); - return Err(anyhow!("Imported version is 'dev', which is not compatible with current version {}. Please use a different version.", current_version)); - } - - let current_major_parsed = extract_major(¤t_version) - .ok_or_else(|| anyhow!("Could not parse current version"))?; - let imported_major_parsed = extract_major(imported_version) - .ok_or_else(|| anyhow!("Could not parse imported version"))?; - - if current_major_parsed < imported_major_parsed { - return Err(anyhow!( - "Version mismatch: Imported version {} is not compatible with current version {}. Please upgrade your system.", - imported_version, - current_version - )); - } - Ok(()) -} - #[instrument(err, skip_all)] pub async fn get_election_event_schema( data_str: &str, @@ -520,7 +476,7 @@ pub async fn get_election_event_schema( tenant_id: String, ) -> Result<(ImportElectionEventSchema, HashMap)> { let original_data: ImportElectionEventSchema = deserialize_str(data_str)?; - let current_version = std::env::var("APP_VERSION").unwrap_or_else(|_| "dev".to_string()); + let current_version = std::env::var(ENV_VAR_APP_VERSION).unwrap_or_else(|_| DEV_APP_VERSION.to_string()); check_version_compatibility(&original_data.version, ¤t_version)?; replace_ids(data_str, &original_data, id, tenant_id.clone()) } diff --git a/packages/windmill/src/services/reports/report_variables.rs b/packages/windmill/src/services/reports/report_variables.rs index 8a0b7c9a23a..e0580b53fde 100644 --- a/packages/windmill/src/services/reports/report_variables.rs +++ b/packages/windmill/src/services/reports/report_variables.rs @@ -24,6 +24,7 @@ use sequent_core::types::hasura::core::{Area, Election, ElectionEvent}; use sequent_core::types::keycloak::AREA_ID_ATTR_NAME; use sequent_core::types::scheduled_event::ScheduledEvent; use sequent_core::util::temp_path::*; +use sequent_core::util::version::{ENV_VAR_APP_HASH, ENV_VAR_APP_VERSION}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::{HashMap, HashSet}; @@ -50,11 +51,11 @@ pub struct ExecutionAnnotations { } pub fn get_app_hash() -> String { - env::var("APP_HASH").unwrap_or("-".to_string()) + env::var(ENV_VAR_APP_HASH).unwrap_or("-".to_string()) } pub fn get_app_version() -> String { - env::var("APP_VERSION").unwrap_or("-".to_string()) + env::var(ENV_VAR_APP_VERSION).unwrap_or("-".to_string()) } #[derive(Debug)] From 256ab80e370dec32ef56541265361beeda04bf22 Mon Sep 17 00:00:00 2001 From: Beltran Rodriguez Date: Wed, 10 Dec 2025 15:26:43 +0000 Subject: [PATCH 5/6] Format --- packages/sequent-core/src/util/mod.rs | 4 +- packages/sequent-core/src/util/version.rs | 37 +++++++++++++------ .../services/export/export_election_event.rs | 5 ++- .../services/import/import_election_event.rs | 7 +++- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/packages/sequent-core/src/util/mod.rs b/packages/sequent-core/src/util/mod.rs index 15f29611257..4b7f51990a8 100644 --- a/packages/sequent-core/src/util/mod.rs +++ b/packages/sequent-core/src/util/mod.rs @@ -7,10 +7,10 @@ pub mod date_time; pub mod integrity_check; pub mod mime; pub mod normalize_vote; -#[cfg(feature = "version")] -pub mod version; #[cfg(feature = "reports")] pub mod temp_path; +#[cfg(feature = "version")] +pub mod version; #[cfg(feature = "reports")] pub mod aws; diff --git a/packages/sequent-core/src/util/version.rs b/packages/sequent-core/src/util/version.rs index 1bf7e7d681f..a3eb7f52f78 100644 --- a/packages/sequent-core/src/util/version.rs +++ b/packages/sequent-core/src/util/version.rs @@ -2,14 +2,17 @@ // // SPDX-License-Identifier: AGPL-3.0-only +use anyhow::{anyhow, Result}; use tracing::{info, instrument}; -use anyhow::{Result, anyhow}; pub const DEV_APP_VERSION: &str = "dev"; pub const ENV_VAR_APP_VERSION: &str = "APP_VERSION"; pub const ENV_VAR_APP_HASH: &str = "APP_HASH"; -pub fn check_version_compatibility(imported_version: &str, current_version: &str) -> Result<()> { +pub fn check_version_compatibility( + imported_version: &str, + current_version: &str, +) -> Result<()> { info!( "Checking version compatibility - Current: {}, Imported: {}", current_version, imported_version @@ -47,7 +50,8 @@ fn extract_major(input: &str) -> Option { // We take characters from the start as long as they are digits. // This stops at the first dot '.', hyphen '-', or any non-digit. - let major_str: String = trimmed.chars().take_while(|c| c.is_ascii_digit()).collect(); + let major_str: String = + trimmed.chars().take_while(|c| c.is_ascii_digit()).collect(); // Parse the result into a u64 // If the string was empty (e.g., input was "invalid"), this returns None. @@ -66,8 +70,13 @@ mod tests { fn test_current_version_is_dev() { // If current system is DEV_APP_VERSION, it should accept anything assert!(check_version_compatibility("1.0.0", DEV_APP_VERSION).is_ok()); - assert!(check_version_compatibility("99.99.99", DEV_APP_VERSION).is_ok()); - assert!(check_version_compatibility(DEV_APP_VERSION, DEV_APP_VERSION).is_ok()); + assert!( + check_version_compatibility("99.99.99", DEV_APP_VERSION).is_ok() + ); + assert!( + check_version_compatibility(DEV_APP_VERSION, DEV_APP_VERSION) + .is_ok() + ); } #[test] @@ -92,7 +101,7 @@ mod tests { // Importing an OLDER version into a NEWER system should be OK // Imported: 1, Current: 2 assert!(check_version_compatibility("1.0.0", "2.0.0").is_ok()); - + // Imported: 10, Current: 11 assert!(check_version_compatibility("10.5.5", "11.0.0").is_ok()); } @@ -111,12 +120,18 @@ mod tests { // Invalid current version let res_current = check_version_compatibility("1.0.0", "invalid_ver"); assert!(res_current.is_err()); - assert!(res_current.unwrap_err().to_string().contains("Could not parse current version")); + assert!(res_current + .unwrap_err() + .to_string() + .contains("Could not parse current version")); // Invalid imported version let res_imported = check_version_compatibility("invalid_ver", "1.0.0"); assert!(res_imported.is_err()); - assert!(res_imported.unwrap_err().to_string().contains("Could not parse imported version")); + assert!(res_imported + .unwrap_err() + .to_string() + .contains("Could not parse imported version")); } #[test] @@ -124,7 +139,7 @@ mod tests { // Handling 'v' or 'V' prefixes // Imported v1 (1) into Current 1 -> OK assert!(check_version_compatibility("v1.0.0", "1.0.0").is_ok()); - + // Imported V2 (2) into Current v1 (1) -> Error assert!(check_version_compatibility("V2.0.0", "v1.0.0").is_err()); } @@ -154,8 +169,8 @@ mod tests { assert_eq!(extract_major("not_a_number"), None); assert_eq!(extract_major(""), None); assert_eq!(extract_major("v"), None); - + // Ensure it stops at non-digits assert_eq!(extract_major("5startswithnumber"), Some(5)); } -} \ No newline at end of file +} diff --git a/packages/windmill/src/services/export/export_election_event.rs b/packages/windmill/src/services/export/export_election_event.rs index c376baeb298..3ca766a4113 100644 --- a/packages/windmill/src/services/export/export_election_event.rs +++ b/packages/windmill/src/services/export/export_election_event.rs @@ -32,7 +32,7 @@ use sequent_core::services::keycloak::KeycloakAdminClient; use sequent_core::services::s3; use sequent_core::types::hasura::core::Election; use sequent_core::types::hasura::core::KeysCeremony; -use sequent_core::util::version::{ENV_VAR_APP_VERSION, DEV_APP_VERSION}; +use sequent_core::util::version::{DEV_APP_VERSION, ENV_VAR_APP_VERSION}; use std::collections::HashMap; use std::env; use std::fs::File; @@ -137,7 +137,8 @@ pub async fn read_export_data( vec![] }; - let version = std::env::var(ENV_VAR_APP_VERSION).unwrap_or_else(|_| DEV_APP_VERSION.to_string()); + let version = + std::env::var(ENV_VAR_APP_VERSION).unwrap_or_else(|_| DEV_APP_VERSION.to_string()); Ok(ImportElectionEventSchema { tenant_id: Uuid::parse_str(&tenant_id)?, diff --git a/packages/windmill/src/services/import/import_election_event.rs b/packages/windmill/src/services/import/import_election_event.rs index 69960ad92b5..48f274c9061 100644 --- a/packages/windmill/src/services/import/import_election_event.rs +++ b/packages/windmill/src/services/import/import_election_event.rs @@ -43,7 +43,9 @@ use sequent_core::types::hasura::core::Document; use sequent_core::types::hasura::core::KeysCeremony; use sequent_core::types::hasura::core::TasksExecution; use sequent_core::util::mime::{get_mime_types, matches_mime}; -use sequent_core::util::version::{ENV_VAR_APP_VERSION, DEV_APP_VERSION, check_version_compatibility}; +use sequent_core::util::version::{ + check_version_compatibility, DEV_APP_VERSION, ENV_VAR_APP_VERSION, +}; use serde::{Deserialize, Serialize}; use serde_json::{json, Map, Value}; use std::collections::HashMap; @@ -476,7 +478,8 @@ pub async fn get_election_event_schema( tenant_id: String, ) -> Result<(ImportElectionEventSchema, HashMap)> { let original_data: ImportElectionEventSchema = deserialize_str(data_str)?; - let current_version = std::env::var(ENV_VAR_APP_VERSION).unwrap_or_else(|_| DEV_APP_VERSION.to_string()); + let current_version = + std::env::var(ENV_VAR_APP_VERSION).unwrap_or_else(|_| DEV_APP_VERSION.to_string()); check_version_compatibility(&original_data.version, ¤t_version)?; replace_ids(data_str, &original_data, id, tenant_id.clone()) } From e5b561438f60965a8ccacd6bdea76b82525df6a5 Mon Sep 17 00:00:00 2001 From: Beltran Rodriguez Date: Fri, 13 Feb 2026 10:28:17 +0000 Subject: [PATCH 6/6] Remove not needed build feature --- packages/sequent-core/Cargo.toml | 1 - packages/sequent-core/src/util/mod.rs | 4 ++-- packages/windmill/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/sequent-core/Cargo.toml b/packages/sequent-core/Cargo.toml index 4f80aadbf3b..7ee9de08c45 100644 --- a/packages/sequent-core/Cargo.toml +++ b/packages/sequent-core/Cargo.toml @@ -147,7 +147,6 @@ probe = ["dep:tokio", "dep:warp"] lambda_inplace = [] lambda_openwhisk = [] lambda_aws_lambda = [] -version = ["dep:tracing"] plugins_wit = ["dep:wasmtime", "dep:wasmtime-wasi"] default_features = ["dep:strand", "dep:num-bigint", "dep:tempfile", "dep:ammonia"] diff --git a/packages/sequent-core/src/util/mod.rs b/packages/sequent-core/src/util/mod.rs index 4b7f51990a8..89f5644a3e8 100644 --- a/packages/sequent-core/src/util/mod.rs +++ b/packages/sequent-core/src/util/mod.rs @@ -7,10 +7,10 @@ pub mod date_time; pub mod integrity_check; pub mod mime; pub mod normalize_vote; +pub mod version; + #[cfg(feature = "reports")] pub mod temp_path; -#[cfg(feature = "version")] -pub mod version; #[cfg(feature = "reports")] pub mod aws; diff --git a/packages/windmill/Cargo.toml b/packages/windmill/Cargo.toml index 2d8ee81b92f..7b721459d17 100644 --- a/packages/windmill/Cargo.toml +++ b/packages/windmill/Cargo.toml @@ -84,7 +84,7 @@ immudb-rs = { path="../immudb-rs" } b3 = { path="../b3", features = ["client"] } electoral-log = { path="../electoral-log" } strand = { path="../strand" } -sequent-core = { path="../sequent-core", features = ["reports", "s3", "keycloak", "log", "probe", "areas", "default_features","plugins_wit", "sqlite", "version"] } +sequent-core = { path="../sequent-core", features = ["reports", "s3", "keycloak", "log", "probe", "areas", "default_features","plugins_wit", "sqlite"] } velvet = { path="../velvet" }