From c2697d78543ec59a072b0561edbe88e62f6c90a3 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Sun, 28 Dec 2025 15:12:14 +0000 Subject: [PATCH] lib: add timestamp to Run For each Run, the timestamp can be easily tracked using the operation's timestamp. This field is added to Run to provide more information to end users. --- src/lib.rs | 62 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 153e242..6200a0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -376,28 +376,34 @@ impl Job { } } - fn update(&mut self, node: NodeId, uuid: Uuid, reason: Reason) -> bool { + fn update( + &mut self, + node: NodeId, + uuid: Uuid, + reason: Reason, + timestamp: cob::Timestamp, + ) -> bool { let Some(runs) = self.runs.get_mut(&node) else { return false; }; let mut updated = false; runs.0.entry(uuid).and_modify(|run| { updated = true; - *run = run.clone().finish(reason); + *run = run.clone().finish(reason, timestamp); }); updated } - fn action(&mut self, node: NodeId, action: Action) { + fn action(&mut self, node: NodeId, action: Action, timestamp: cob::Timestamp) { match action { // Cannot request for another `oid`, so we ignore any superfluous // request actions Action::Request { .. } => {} Action::Run { uuid, log } => { - self.insert(node, uuid, Run::new(log)); + self.insert(node, uuid, Run::new(log, timestamp)); } Action::Finished { uuid, reason } => { - self.update(node, uuid, reason); + self.update(node, uuid, reason, timestamp); } } } @@ -422,7 +428,7 @@ impl store::Cob for Job { .map_err(|err| error::Build::MissingCommit { oid, err })?; let mut runs = Self::new(oid); for action in actions { - runs.action(op.author, action); + runs.action(op.author, action, op.timestamp); } Ok(runs) } @@ -434,7 +440,7 @@ impl store::Cob for Job { _repo: &R, ) -> Result<(), Self::Error> { for action in op.actions { - self.action(op.author, action); + self.action(op.author, action, op.timestamp); } Ok(()) } @@ -469,7 +475,7 @@ impl Evaluate for Job { /// /// The `Run` also contains a [`Url`] so that any extra metadata, for example /// logs, can be tracked outside of the `Run` itself. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Run { /// The status of the run. /// @@ -480,23 +486,27 @@ pub struct Run { status: Status, /// The [`Url`] of the [`Run`] where information is logged by the node. log: Url, + /// The timestamp of the last operation to affect the run. + timestamp: cob::Timestamp, } impl Run { /// Create a new `Run` with a [`Url`] that ideally points to the log of that /// process. - pub fn new(log: Url) -> Self { + pub fn new(log: Url, timestamp: cob::Timestamp) -> Self { Self { status: Status::Started, log, + timestamp, } } /// Mark the `Run` as finished. - fn finish(self, reason: Reason) -> Self { + fn finish(self, reason: Reason, timestamp: cob::Timestamp) -> Self { Self { status: Status::Finished(reason), log: self.log, + timestamp, } } @@ -510,6 +520,11 @@ impl Run { &self.status } + /// Return the timestamp of the last update to the `Run`. + pub fn timestamp(&self) -> &cob::Timestamp { + &self.timestamp + } + /// Returns `true` if the status of the `Run` is [`Status::Started`]. pub fn is_started(&self) -> bool { match self.status { @@ -909,23 +924,17 @@ mod test { let (bob_uuid, bob_log) = node_run(); job.run(bob_uuid, bob_log.clone(), &bob.signer).unwrap(); - let alice_runs = job.runs_of(alice.signer.public_key()); - assert!(alice_runs.is_some()); - assert_eq!( - *alice_runs.unwrap(), - [(alice_uuid, Run::new(alice_log))] - .into_iter() - .collect::() - ); + let alice_runs = job.runs_of(alice.signer.public_key()).unwrap(); + assert!(alice_runs.contains_key(&alice_uuid)); + let run = alice_runs.get(&alice_uuid).unwrap(); + assert_eq!(run.status, Status::Started); + assert_eq!(run.log, alice_log); - let bob_runs = job.runs_of(bob.signer.public_key()); - assert!(bob_runs.is_some()); - assert_eq!( - *bob_runs.unwrap(), - [(bob_uuid, Run::new(bob_log))] - .into_iter() - .collect::() - ); + let bob_runs = job.runs_of(bob.signer.public_key()).unwrap(); + assert!(bob_runs.contains_key(&bob_uuid)); + let run = bob_runs.get(&bob_uuid).unwrap(); + assert_eq!(run.status, Status::Started); + assert_eq!(run.log, bob_log); job.finish(alice_uuid, Reason::Succeeded, &alice.signer) .unwrap(); @@ -987,6 +996,7 @@ mod test { Run { status: Status::Started, log: Url::parse("https://example.com/ci/logs").unwrap(), + timestamp: radicle::cob::Timestamp::from_secs(1358182), }, ); }