hive-forge: add ci-runs listing verb, fix ci-log's ambiguous no-log message
This commit is contained in:
parent
55eaebc9cf
commit
56c0602c2f
6 changed files with 204 additions and 59 deletions
|
|
@ -20,10 +20,10 @@
|
|||
//! - `--pr <n>` → the PR's head branch; dispatches `--workflow` (default
|
||||
//! `ci.yml`) on it.
|
||||
//! - `--branch <name>` → dispatches `--workflow` on that branch directly.
|
||||
//! - `--run <n>` → looks the run up in the Actions runs list (by the
|
||||
//! `runs/<n>` tail of its `html_url`, same convention as `ci-log` /
|
||||
//! `artifact-get`) and dispatches the SAME workflow on the SAME ref the
|
||||
//! run used (the run record's `prettyref` + `workflow_id`).
|
||||
//! - `--run <n>` → looks the run up by its display number (same convention
|
||||
//! as `ci-log` / `artifact-get` / `ci-runs`) and dispatches the SAME
|
||||
//! workflow on the SAME ref the run used (the run record's `prettyref` +
|
||||
//! `workflow_id`).
|
||||
//!
|
||||
//! Dispatch re-runs the whole workflow (no single-job variant). ⚠️ `--pr`
|
||||
//! verifies the code but doesn't reliably move the PR's own status
|
||||
|
|
@ -31,8 +31,9 @@
|
|||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use clap::Args as ClapArgs;
|
||||
use forgejo_api::structs::{ActionRun, DispatchWorkflowOption, ListActionRunsQuery};
|
||||
use forgejo_api::structs::{ActionRun, DispatchWorkflowOption};
|
||||
|
||||
use super::ci_common::find_run_by_number;
|
||||
use crate::client::{Client, index};
|
||||
|
||||
#[derive(ClapArgs)]
|
||||
|
|
@ -107,47 +108,19 @@ fn branch_for_pr(client: &Client, pr: u64) -> Result<String> {
|
|||
.with_context(|| format!("ci-rerun: PR #{pr} has no head.ref"))
|
||||
}
|
||||
|
||||
/// Page the Actions runs list (newest-first) to find the run whose run-page
|
||||
/// `html_url` ends in `/runs/<run-number>`, returning the `(workflow, branch)`
|
||||
/// to dispatch a fresh run of it. `fallback_workflow` is used when the run
|
||||
/// carries no workflow file name.
|
||||
/// Resolve the run whose display number is `run_number` to the
|
||||
/// `(workflow, branch)` to dispatch a fresh run of it. `fallback_workflow`
|
||||
/// is used when the run carries no workflow file name.
|
||||
fn resolve_run(
|
||||
client: &Client,
|
||||
repo: &str,
|
||||
run_number: u64,
|
||||
fallback_workflow: &str,
|
||||
) -> Result<(String, String)> {
|
||||
const PER_PAGE: u32 = 50;
|
||||
const MAX_PAGES: u32 = 40;
|
||||
let (owner, name) = client.owner_repo()?;
|
||||
for page in 1..=MAX_PAGES {
|
||||
let body = client
|
||||
.api()
|
||||
.list_action_runs(owner, name, ListActionRunsQuery::default())
|
||||
.page(page)
|
||||
.page_size(PER_PAGE)
|
||||
.send()?;
|
||||
let runs = body.workflow_runs.unwrap_or_default();
|
||||
if runs.is_empty() {
|
||||
break;
|
||||
}
|
||||
for run in &runs {
|
||||
if run_number_of(run) == Some(run_number) {
|
||||
return run_dispatch_target(run, fallback_workflow)
|
||||
.with_context(|| format!("ci-rerun: run #{run_number} has no ref"));
|
||||
}
|
||||
}
|
||||
}
|
||||
bail!("ci-rerun: run #{run_number} not found in {repo} (no matching workflow run)");
|
||||
}
|
||||
|
||||
/// The per-repo run NUMBER from a run object's `html_url` (`…/runs/<n>` tail),
|
||||
/// matching the `runs/<n>` the UI shows and `pr-status` surfaces.
|
||||
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())
|
||||
let run = find_run_by_number(client, run_number)?
|
||||
.with_context(|| format!("ci-rerun: run #{run_number} not found in {repo}"))?;
|
||||
run_dispatch_target(&run, fallback_workflow)
|
||||
.with_context(|| format!("ci-rerun: run #{run_number} has no ref"))
|
||||
}
|
||||
|
||||
/// Pull the `(workflow-file, ref)` dispatch target out of a run record:
|
||||
|
|
@ -168,7 +141,7 @@ fn run_dispatch_target(run: &ActionRun, fallback_workflow: &str) -> Option<(Stri
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{ActionRun, run_dispatch_target, run_number_of};
|
||||
use super::{ActionRun, run_dispatch_target};
|
||||
use serde_json::json;
|
||||
|
||||
/// Build a typed run record from an API-shaped JSON fixture. The
|
||||
|
|
@ -184,19 +157,6 @@ mod tests {
|
|||
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));
|
||||
let run = run_from(json!({ "html_url": "https://forge/o/r/actions/runs/42" }));
|
||||
assert_eq!(run_number_of(&run), Some(42));
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_workflow_and_branch() {
|
||||
let run = run_from(json!({
|
||||
|
|
|
|||
Loading…
Reference in a new issue