diff --git a/src/state/state_common.rs b/src/state/state_common.rs
index ab1bbb7..4414544 100644
@@ -2,6 +2,7 @@ use anyhow::{Context, Result};
use log;
use regex::Regex;
use serde::{Deserialize, Serialize};
+use sqlx::Row;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
@@ -67,6 +68,38 @@ impl StateConfig {
}
}
+pub fn calculate_workspace_version(repo_root: &Path) -> Result<VersionInfo> {
+ if tokio::runtime::Handle::try_current().is_ok() {
+ anyhow::bail!(
+ "calculate_workspace_version cannot be called from an async runtime; use calculate_workspace_version_async"
+ );
+ }
+ let rt = tokio::runtime::Runtime::new()?;
+ rt.block_on(calculate_workspace_version_async(repo_root))
+}
+
+pub async fn calculate_workspace_version_async(repo_root: &Path) -> Result<VersionInfo> {
+ let major_version = load_project_major_version_async(repo_root).await?;
+ VersionInfo::calculate_with_major(major_version)
+}
+
+async fn load_project_major_version_async(repo_root: &Path) -> Result<u32> {
+ let db_path = repo_root.join(".wsb/project.db");
+ if let Some(parent) = db_path.parent() {
+ fs::create_dir_all(parent)?;
+ }
+ load_state_config_from_db(&db_path).await?;
+ let database_url = format!("sqlite:{}", db_path.display());
+ let pool = sqlx::SqlitePool::connect(&database_url).await?;
+ let row = sqlx::query("SELECT major_version FROM projects LIMIT 1")
+ .fetch_optional(&pool)
+ .await?;
+
+ Ok(row
+ .map(|row| row.get::<i64, _>("major_version") as u32)
+ .unwrap_or(0))
+}
+
#[derive(Debug, Clone)]
pub struct VersionInfo {
pub major_version: String,
@@ -840,6 +873,10 @@ fn update_julia_project(content: &str, version: &str) -> Result<String> {
// Database integration functions for StateConfig
async fn load_state_config_from_db(db_path: &std::path::Path) -> Result<StateConfig> {
use sqlx::{SqlitePool, migrate::MigrateDatabase, Row};
+
+ if let Some(parent) = db_path.parent() {
+ fs::create_dir_all(parent)?;
+ }
// Ensure database exists and is initialized
let database_url = format!("sqlite:{}", db_path.display());
@@ -1354,6 +1391,46 @@ set(CMAKE_CXX_STANDARD 17)
assert_eq!(content.trim(), "1.0.5.100");
}
+ fn run_git(repo_root: &Path, args: &[&str]) {
+ let status = Command::new("git")
+ .args(args)
+ .current_dir(repo_root)
+ .status()
+ .unwrap();
+ assert!(status.success(), "git command failed: {:?}", args);
+ }
+
+ #[tokio::test]
+ async fn test_calculate_workspace_version_uses_project_major_version_from_db() {
+ let temp_dir = TempDir::new().unwrap();
+ let repo_root = temp_dir.path();
+
+ run_git(repo_root, &["init"]);
+ run_git(repo_root, &["config", "user.name", "Test User"]);
+ run_git(repo_root, &["config", "user.email", "test@example.com"]);
+
+ fs::write(repo_root.join("README.md"), "hello\n").unwrap();
+ run_git(repo_root, &["add", "README.md"]);
+ run_git(repo_root, &["commit", "-m", "initial"]);
+
+ let _ = StateConfig::load_async(repo_root).await.unwrap();
+ let db_path = repo_root.join(".wsb/project.db");
+ let database_url = format!("sqlite:{}", db_path.display());
+ let pool = sqlx::SqlitePool::connect(&database_url).await.unwrap();
+ sqlx::query("UPDATE projects SET major_version = 7 WHERE id = (SELECT id FROM projects LIMIT 1)")
+ .execute(&pool)
+ .await
+ .unwrap();
+
+ let expected = VersionInfo::calculate_with_major(7).unwrap();
+ let actual = calculate_workspace_version_async(repo_root)
+ .await
+ .unwrap();
+
+ assert_eq!(actual.major_version, "v7");
+ assert_eq!(actual.full_version, expected.full_version);
+ }
+
#[test]
fn test_update_build_gradle_kts() {
let content = r#"plugins {