feat: Update zinit-client dependency to 0.4.0

- Upgrade `zinit-client` dependency to version 0.4.0 across all
  relevant crates. This resolves potential compatibility issues
  and incorporates bug fixes and improvements from the latest
  release.

- Improve error handling and logging in `zinit-client` and
  `service_manager` to provide more informative feedback and
  prevent potential hangs during log retrieval.  Add timeout to
  prevent indefinite blocking on log retrieval.

- Update `publish-all.sh` script to correctly handle the
  `service_manager` crate during publishing.  Improves handling of
  special cases in the publishing script.

- Add `zinit-client.workspace = true` to `Cargo.toml` to ensure
  consistent dependency management across the workspace.  This
  ensures the correct version of `zinit-client` is used everywhere.
This commit is contained in:
Mahmoud-Emad 2025-07-10 11:27:59 +03:00
parent fc2830da31
commit 423b7bfa7e
7 changed files with 84 additions and 35 deletions

View File

@ -83,7 +83,7 @@ windows = { version = "0.61.1", features = [
] } ] }
# Specialized dependencies # Specialized dependencies
zinit-client = "0.3.0" zinit-client = "0.4.0"
urlencoding = "2.1.3" urlencoding = "2.1.3"
tokio-test = "0.4.4" tokio-test = "0.4.4"
@ -106,6 +106,7 @@ sal-postgresclient = { path = "postgresclient", optional = true }
sal-vault = { path = "vault", optional = true } sal-vault = { path = "vault", optional = true }
sal-rhai = { path = "rhai", optional = true } sal-rhai = { path = "rhai", optional = true }
sal-service-manager = { path = "service_manager", optional = true } sal-service-manager = { path = "service_manager", optional = true }
zinit-client.workspace = true
[features] [features]
default = [] default = []

View File

@ -11,20 +11,20 @@ let tests_passed = 0;
// Helper function to run a test // Helper function to run a test
fn run_test(test_name, test_file) { fn run_test(test_name, test_file) {
tests_run += 1;
print(`🔄 Running ${test_name}...`); print(`🔄 Running ${test_name}...`);
try { try {
// In a real implementation, this would execute the test file // In a real implementation, this would execute the test file
// For now, we'll simulate successful test execution // For now, we'll simulate successful test execution
print(` 📁 Loading: ${test_file}`); print(` 📁 Loading: ${test_file}`);
print(` ✅ ${test_name} completed successfully`); print(` ✅ ${test_name} completed successfully`);
tests_passed += 1; print("");
return true; // Return success
} catch (error) { } catch (error) {
print(` ❌ ${test_name} failed: ${error}`); print(` ❌ ${test_name} failed: ${error}`);
print("");
return false; // Return failure
} }
print("");
} }
// Execute all service manager tests // Execute all service manager tests
@ -35,9 +35,20 @@ print("3. Cross-Platform Compatibility Test");
print(""); print("");
// Run individual tests // Run individual tests
run_test("Service Lifecycle Test", "01_service_lifecycle.rhai"); tests_run += 1;
run_test("Circle Worker Deployment Test", "02_circle_worker_deployment.rhai"); if run_test("Service Lifecycle Test", "01_service_lifecycle.rhai") {
run_test("Cross-Platform Compatibility Test", "03_cross_platform_compatibility.rhai"); tests_passed += 1;
}
tests_run += 1;
if run_test("Circle Worker Deployment Test", "02_circle_worker_deployment.rhai") {
tests_passed += 1;
}
tests_run += 1;
if run_test("Cross-Platform Compatibility Test", "03_cross_platform_compatibility.rhai") {
tests_passed += 1;
}
// Test summary // Test summary
print("📊 Test Summary:"); print("📊 Test Summary:");

View File

@ -147,10 +147,12 @@ is_published() {
local crate_name="$1" local crate_name="$1"
local version="$2" local version="$2"
# Handle special case for zinit_client (directory) -> sal-zinit-client (package) # Handle special cases for directory names that differ from published package names
local package_name="sal-$crate_name" local package_name="sal-$crate_name"
if [ "$crate_name" = "zinit_client" ]; then if [ "$crate_name" = "zinit_client" ]; then
package_name="sal-zinit-client" package_name="sal-zinit-client"
elif [ "$crate_name" = "service_manager" ]; then
package_name="sal-service-manager"
fi fi
# Use cargo search to check if the exact version exists # Use cargo search to check if the exact version exists
@ -215,10 +217,12 @@ for crate in "${CRATES[@]}"; do
# Check if already published # Check if already published
if [ "$DRY_RUN" = false ] && is_published "$crate" "$VERSION"; then if [ "$DRY_RUN" = false ] && is_published "$crate" "$VERSION"; then
# Handle special case for zinit_client display name # Handle special cases for display names
display_name="sal-$crate" display_name="sal-$crate"
if [ "$crate" = "zinit_client" ]; then if [ "$crate" = "zinit_client" ]; then
display_name="sal-zinit-client" display_name="sal-zinit-client"
elif [ "$crate" = "service_manager" ]; then
display_name="sal-service-manager"
fi fi
echo -e "${GREEN}$display_name@$VERSION already published, skipping${NC}" echo -e "${GREEN}$display_name@$VERSION already published, skipping${NC}"
echo "" echo ""

View File

@ -17,7 +17,7 @@ serde_json = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
once_cell = { workspace = true } once_cell = { workspace = true }
# Use base zinit-client instead of SAL wrapper # Use base zinit-client instead of SAL wrapper
zinit-client = { version = "0.3.0" } zinit-client = { version = "0.4.0" }
# Optional Rhai integration # Optional Rhai integration
rhai = { workspace = true, optional = true } rhai = { workspace = true, optional = true }

View File

@ -306,6 +306,8 @@ impl ServiceManager for ZinitServiceManager {
let logs = self let logs = self
.execute_async(async move { .execute_async(async move {
use futures::StreamExt; use futures::StreamExt;
use tokio::time::{timeout, Duration};
let mut log_stream = client let mut log_stream = client
.logs(false, Some(service_name_owned.as_str())) .logs(false, Some(service_name_owned.as_str()))
.await?; .await?;
@ -314,18 +316,32 @@ impl ServiceManager for ZinitServiceManager {
// Collect logs from the stream with a reasonable limit // Collect logs from the stream with a reasonable limit
let mut count = 0; let mut count = 0;
const MAX_LOGS: usize = 100; const MAX_LOGS: usize = 100;
const LOG_TIMEOUT: Duration = Duration::from_secs(5);
while let Some(log_result) = log_stream.next().await { // Use timeout to prevent hanging
match log_result { let result = timeout(LOG_TIMEOUT, async {
Ok(log_entry) => { while let Some(log_result) = log_stream.next().await {
logs.push(format!("{:?}", log_entry)); match log_result {
count += 1; Ok(log_entry) => {
if count >= MAX_LOGS { logs.push(format!("{:?}", log_entry));
break; count += 1;
if count >= MAX_LOGS {
break;
}
} }
Err(_) => break,
} }
Err(_) => break,
} }
})
.await;
// Handle timeout - this is not an error, just means no more logs available
if result.is_err() {
log::debug!(
"Log reading timed out after {} seconds, returning {} logs",
LOG_TIMEOUT.as_secs(),
logs.len()
);
} }
Ok::<Vec<String>, ZinitError>(logs) Ok::<Vec<String>, ZinitError>(logs)

View File

@ -18,7 +18,7 @@ thiserror = "2.0.12"
tokio = { version = "1.45.0", features = ["full"] } tokio = { version = "1.45.0", features = ["full"] }
# Zinit client # Zinit client
zinit-client = "0.3.0" zinit-client = "0.4.0"
# Rhai integration # Rhai integration
rhai = { version = "1.12.0", features = ["sync"] } rhai = { version = "1.12.0", features = ["sync"] }

View File

@ -149,34 +149,51 @@ impl ZinitClientWrapper {
// Get logs with real implementation // Get logs with real implementation
pub async fn logs(&self, filter: Option<String>) -> Result<Vec<String>, ZinitError> { pub async fn logs(&self, filter: Option<String>) -> Result<Vec<String>, ZinitError> {
use futures::StreamExt; use futures::StreamExt;
use tokio::time::{timeout, Duration};
// The logs method requires a follow parameter and filter // The logs method requires a follow parameter and filter
let follow = false; // Don't follow logs, just get existing ones let follow = false; // Don't follow logs, just get existing ones
let mut log_stream = self.client.logs(follow, filter).await?; let mut log_stream = self.client.logs(follow, filter).await?;
let mut logs = Vec::new(); let mut logs = Vec::new();
// Collect logs from the stream with a reasonable limit // Collect logs from the stream with a reasonable limit and timeout
let mut count = 0; let mut count = 0;
const MAX_LOGS: usize = 1000; const MAX_LOGS: usize = 1000;
const LOG_TIMEOUT: Duration = Duration::from_secs(5);
while let Some(log_result) = log_stream.next().await { // Use timeout to prevent hanging
match log_result { let result = timeout(LOG_TIMEOUT, async {
Ok(log_entry) => { while let Some(log_result) = log_stream.next().await {
// Convert LogEntry to String using Debug formatting match log_result {
logs.push(format!("{:?}", log_entry)); Ok(log_entry) => {
count += 1; // Convert LogEntry to String using Debug formatting
if count >= MAX_LOGS { logs.push(format!("{:?}", log_entry));
count += 1;
if count >= MAX_LOGS {
break;
}
}
Err(e) => {
log::warn!("Error reading log entry: {}", e);
break; break;
} }
} }
Err(e) => { }
log::warn!("Error reading log entry: {}", e); })
break; .await;
}
// Handle timeout - this is not an error, just means no more logs available
match result {
Ok(_) => Ok(logs),
Err(_) => {
log::debug!(
"Log reading timed out after {} seconds, returning {} logs",
LOG_TIMEOUT.as_secs(),
logs.len()
);
Ok(logs)
} }
} }
Ok(logs)
} }
} }