hyperhive/hive-forge/src/verbs/ci_common.rs

73 lines
2.6 KiB
Rust

//! 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/<n>`
/// tail) — the number the UI shows, `pr-status` surfaces, and every
/// `--run <n>` 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<u64> {
run.html_url
.as_ref()
.and_then(|u| u.as_str().rsplit('/').next())
.and_then(|s| s.parse::<u64>().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<Option<ActionRun>> {
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);
}
}