Skip to main content

soma_self_update/
transaction_async.rs

1use std::path::PathBuf;
2
3use super::{ConfirmationOutcome, InstallOutcome};
4use crate::{MigrationOutcome, RecoveryAction, Result, UpdateError, Updater, ValidatedArtifact};
5
6impl Updater {
7    /// Moves this executable's durable state authority to a new idle marker path.
8    ///
9    /// An initial migration refuses to run while any marker, marker temporary,
10    /// staged artifact, or rollback artifact exists. Once authority already
11    /// names the destination, a retry only confirms the directory boundary and
12    /// does not reject transaction state created through the returned updater.
13    /// Both success variants carry the
14    /// updater bound to the new state path; callers must retain it even when the
15    /// authority rename's directory sync is reported as indeterminate. Retrying
16    /// the same migration is idempotent.
17    pub async fn migrate_state_file(
18        &self,
19        new_state_file: impl Into<PathBuf>,
20    ) -> Result<MigrationOutcome> {
21        let updater = self.clone();
22        let new_state_file = new_state_file.into();
23        let error_path = new_state_file.clone();
24        blocking_transaction(error_path, move || {
25            updater.migrate_state_file_sync(new_state_file)
26        })
27        .await
28    }
29
30    pub async fn install(
31        &self,
32        validated: ValidatedArtifact,
33        previous_version: impl Into<String>,
34    ) -> Result<InstallOutcome> {
35        let updater = self.clone();
36        let error_path = self.layout().state_file().to_path_buf();
37        let previous = previous_version.into();
38        blocking_transaction(error_path, move || {
39            updater.install_sync(validated, previous)
40        })
41        .await
42    }
43
44    pub async fn recover_on_startup(&self, running_version: &str) -> Result<RecoveryAction> {
45        let updater = self.clone();
46        let error_path = self.layout().state_file().to_path_buf();
47        let running_version = running_version.to_owned();
48        blocking_transaction(error_path, move || {
49            updater.recover_on_startup_sync(&running_version)
50        })
51        .await
52    }
53
54    pub async fn confirm_success(&self, running_version: &str) -> Result<ConfirmationOutcome> {
55        let updater = self.clone();
56        let error_path = self.layout().state_file().to_path_buf();
57        let running_version = running_version.to_owned();
58        blocking_transaction(error_path, move || {
59            updater.confirm_success_sync(&running_version)
60        })
61        .await
62    }
63}
64
65async fn blocking_transaction<T, F>(error_path: PathBuf, operation: F) -> Result<T>
66where
67    T: Send + 'static,
68    F: FnOnce() -> Result<T> + Send + 'static,
69{
70    tokio::task::spawn_blocking(operation)
71        .await
72        .map_err(|error| {
73            UpdateError::io(
74                error_path,
75                std::io::Error::other(format!("blocking update transaction failed: {error}")),
76            )
77        })?
78}