Radish alpha
r
rad:z2UcCU1LgMshWvXj6hXSDDrwB8q8M
Radicle Job Collaborative Object
Radicle
Git
feat: add an action to set the log of a run to another URL
Open levitte opened 1 month ago

This allows storing the log as a commit in a repository, and referring to that commit by commit id.

This also allows starting with a live / interactive log while the run is running, and change that to a stored log when the run is done.

2 files changed +81 -3 8799f728 ae8ca10b
modified src/bin/rad-job.rs
@@ -187,7 +187,7 @@ where
}

fn succeeded_job<G>(
-
    command::Succeeded { oid, run }: command::Succeeded,
+
    command::Succeeded { oid, run, url }: command::Succeeded,
    jobs: &mut Jobs<'_, Repository>,
    signer: &Device<G>,
) -> Result<(), error::Succeeded>
@@ -198,11 +198,15 @@ where
    let mut job = JobMut::new(id, job, jobs);
    job.finish(run, Reason::Succeeded, signer)
        .map_err(|err| error::Succeeded::Store { id, err })?;
+
    if let Some(v) = url {
+
        job.set_log(run, v, signer)
+
            .map_err(|err| error::Succeeded::Store { id, err })?;
+
    }
    Ok(())
}

fn failed_job<G>(
-
    command::Failed { oid, run }: command::Failed,
+
    command::Failed { oid, run, url }: command::Failed,
    jobs: &mut Jobs<'_, Repository>,
    signer: &Device<G>,
) -> Result<(), error::Failed>
@@ -213,6 +217,10 @@ where
    let mut job = JobMut::new(id, job, jobs);
    job.finish(run, Reason::Failed, signer)
        .map_err(|err| error::Failed::Store { id, err })?;
+
    if let Some(v) = url {
+
        job.set_log(run, v, signer)
+
            .map_err(|err| error::Failed::Store { id, err })?;
+
    }
    Ok(())
}

@@ -368,7 +376,7 @@ mod command {
    pub struct Run {
        /// Git commit which the run is processing.
        pub oid: Oid,
-
        /// URL to information about the run, such a a run log.
+
        /// URL to information about the run, e.g. a live run log.
        pub url: Url,
    }

@@ -379,6 +387,8 @@ mod command {
        pub oid: Oid,
        /// Run identifier, a UUID.
        pub run: Uuid,
+
        /// URL to information about the run, e.g. a saved run log.
+
        pub url: Option<Url>,
    }

    /// Mark a run as having finished successfully.
@@ -388,6 +398,8 @@ mod command {
        pub oid: Oid,
        /// Run identifier, a UUID.
        pub run: Uuid,
+
        /// URL to information about the run, e.g. a saved run log.
+
        pub url: Option<Url>,
    }
}

modified src/lib.rs
@@ -276,6 +276,13 @@ pub enum Action {
        /// The [`Reason`] which the node finished with.
        reason: Reason,
    },
+
    /// Change the [`Url`] of the [`Run`]
+
    SetLog {
+
        /// The [`Uuid`] that identifies the [`Run`] to set the log URL for.
+
        uuid: Uuid,
+
        /// The [`Url`] where the node will log any information or data.
+
        log: Url,
+
    },
}

impl CobAction for Action {
@@ -394,6 +401,20 @@ impl Job {
        updated
    }

+
    fn set_log(&mut self, node: NodeId, uuid: Uuid, log: Url, timestamp: cob::Timestamp) -> bool {
+
        match self
+
            .runs
+
            .get_mut(&node)
+
            .and_then(|runs| runs.0.get_mut(&uuid))
+
        {
+
            Some(run) => {
+
                run.set_log(log, timestamp);
+
                true
+
            }
+
            None => false,
+
        }
+
    }
+

    fn action(&mut self, node: NodeId, action: Action, timestamp: cob::Timestamp) {
        match action {
            // Cannot request for another `oid`, so we ignore any superfluous
@@ -405,6 +426,9 @@ impl Job {
            Action::Finished { uuid, reason } => {
                self.update(node, uuid, reason, timestamp);
            }
+
            Action::SetLog { uuid, log } => {
+
                self.set_log(node, uuid, log, timestamp);
+
            }
        }
    }
}
@@ -510,6 +534,14 @@ impl Run {
        }
    }

+
    /// Set the log of the `Run`.
+
    ///
+
    /// The timestamp is updated to reflect that the run was modified at the given time.
+
    fn set_log(&mut self, log: Url, timestamp: cob::Timestamp) {
+
        self.log = log;
+
        self.timestamp = timestamp;
+
    }
+

    /// Return URL for the log of this run.
    pub fn log(&self) -> &Url {
        &self.log
@@ -783,6 +815,20 @@ where
        self.transaction("Finished node job", signer, |tx| tx.finish(uuid, reason))
    }

+
    /// Set the log of a [`Run`], identified by the given [`Uuid`], for the node, with
+
    /// the provided [`Url`].
+
    pub fn set_log<G>(
+
        &mut self,
+
        uuid: Uuid,
+
        log: Url,
+
        signer: &Device<G>,
+
    ) -> Result<EntryId, store::Error>
+
    where
+
        G: Signer<crypto::Signature>,
+
    {
+
        self.transaction("Finished node job", signer, |tx| tx.set_log(uuid, log))
+
    }
+

    /// Apply COB operations to a `JobMut`.
    fn transaction<G, F>(
        &mut self,
@@ -874,6 +920,11 @@ where
    fn finish(&mut self, uuid: Uuid, reason: Reason) -> Result<(), store::Error> {
        self.0.push(Action::Finished { uuid, reason })
    }
+

+
    /// Set the log URL.
+
    fn set_log(&mut self, uuid: Uuid, log: Url) -> Result<(), store::Error> {
+
        self.0.push(Action::SetLog { uuid, log })
+
    }
}

#[cfg(test)]
@@ -944,6 +995,11 @@ mod test {

        job.finish(bob_uuid, Reason::Failed, &bob.signer).unwrap();

+
        // At the moment of writing, this is a real URI.
+
        let bob_finish_log = Url::parse("rad:z2UcCU1LgMshWvXj6hXSDDrwB8q8M/z6Mku8hpprWTmCv3BqkssCYDfr2feUdyLSUnycVajFo9XVAx/blob/45026296715e2645546d3818c255679f14aa0761?path=run-log.md").unwrap();
+
        job.set_log(bob_uuid, bob_finish_log.clone(), &bob.signer)
+
            .unwrap();
+

        let succeeded = job.succeeded();
        assert!(succeeded.contains_key(alice.signer.public_key()));
        assert!(!succeeded.contains_key(bob.signer.public_key()));
@@ -952,6 +1008,16 @@ mod test {
        assert!(failed.contains_key(bob.signer.public_key()));
        let started = job.started();
        assert!(started.is_empty());
+

+
        // Check that we still have alice's initial log
+
        let alice_runs = job.runs_of(alice.signer.public_key()).unwrap();
+
        let run = alice_runs.get(&alice_uuid).unwrap();
+
        assert_eq!(run.log, alice_log);
+

+
        // Check that we have bob's finishing log
+
        let bob_runs = job.runs_of(bob.signer.public_key()).unwrap();
+
        let run = bob_runs.get(&bob_uuid).unwrap();
+
        assert_eq!(run.log, bob_finish_log);
    }

    #[test]