//! Shared helpers for the CI Actions run verbs (`ci-log`, `ci-rerun`, //! `ci-runs`). use anyhow::Result; use forgejo_api::structs::{ActionRun, ListActionRunsQuery}; use crate::client::Client; /// The per-repo run NUMBER from a run object's `html_url` (`…/runs/` /// tail) — the number the UI shows, `pr-status` surfaces, and every /// `--run ` flag takes. /// /// ⚠️ NOT [`ActionRun::id`]: Forgejo's `GET /// /repos/{owner}/{repo}/actions/runs/{run_id}` endpoint takes that /// internal, cross-repo id, not this number — confirmed empirically /// (a run with `id: 3284` had display number `3092`). Don't be tempted to /// pass a `--run` value there. pub(crate) fn run_number_of(run: &ActionRun) -> Option { run.html_url .as_ref() .and_then(|u| u.as_str().rsplit('/').next()) .and_then(|s| s.parse::().ok()) } /// Look up a single run by its display number via `ListActionRunsQuery`'s /// `run_number` filter — one request, not a page scan. Confirmed /// empirically that the filter matches on the same number /// [`run_number_of`] reads back out (both trace to `index_in_repo` /// server-side). /// /// `Ok(None)` means the number doesn't exist in `repo` (a fresh dispatch /// still queued, a typo, or a purged/deleted run); `Err` is a genuine /// transport failure. pub(crate) fn find_run_by_number(client: &Client, run_number: u64) -> Result> { let (owner, name) = client.owner_repo()?; let body = client .api() .list_action_runs( owner, name, ListActionRunsQuery { run_number: Some(i64::try_from(run_number).unwrap_or(i64::MAX)), ..Default::default() }, ) .send()?; Ok(body.workflow_runs.unwrap_or_default().into_iter().next()) } #[cfg(test)] mod tests { use super::run_number_of; use serde_json::json; fn run_from(mut v: serde_json::Value) -> forgejo_api::structs::ActionRun { let obj = v.as_object_mut().unwrap(); for key in ["created", "started", "stopped", "updated", "html_url"] { obj.entry(key).or_insert(serde_json::Value::Null); } serde_json::from_value(v).unwrap() } #[test] fn parses_run_number_from_html_url() { let run = run_from(json!({ "html_url": "http://forge/h/h/actions/runs/750" })); assert_eq!(run_number_of(&run), Some(750)); assert_eq!( run_number_of(&run_from(json!({ "html_url": "http://forge/o/r/x" }))), None ); assert_eq!(run_number_of(&run_from(json!({}))), None); } }