From d7bae0d437cf8d3355207abd5472c1a6ef2101bc Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Tue, 11 Aug 2026 17:13:15 -0400 Subject: [PATCH 1/2] Use gh api for workflow watch --- tools/ci/src/workflow_watch.rs | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/tools/ci/src/workflow_watch.rs b/tools/ci/src/workflow_watch.rs index 38ac3b59d5d..efdbd208d52 100644 --- a/tools/ci/src/workflow_watch.rs +++ b/tools/ci/src/workflow_watch.rs @@ -6,40 +6,15 @@ use serde::Deserialize; struct WorkflowRunView { status: String, conclusion: Option, - jobs: Vec, -} - -#[derive(Deserialize)] -struct WorkflowJobView { - name: String, - status: String, - conclusion: Option, } fn get_workflow_run(repo: &str, run_id: u64) -> Result { - let raw = cmd!( - "gh", - "run", - "view", - run_id.to_string(), - "--repo", - repo, - "--json", - "status,conclusion,jobs", - ) - .read() - .with_context(|| format!("failed to read workflow run {run_id} in {repo}"))?; + let raw = cmd!("gh", "api", format!("repos/{repo}/actions/runs/{run_id}")) + .read() + .with_context(|| format!("failed to read workflow run {run_id} in {repo}"))?; serde_json::from_str(&raw).with_context(|| format!("failed to parse workflow run {run_id} in {repo}")) } -fn print_workflow_job_summary(run: &WorkflowRunView) { - println!("Job summary:"); - for job in &run.jobs { - let result = job.conclusion.as_deref().unwrap_or(&job.status); - println!(" {result:>11} {}", job.name); - } -} - pub(crate) fn watch_workflow_run( repo: &str, run_id: u64, @@ -53,7 +28,6 @@ pub(crate) fn watch_workflow_run( attempts += 1; let run = get_workflow_run(repo, run_id)?; if run.status == "completed" { - print_workflow_job_summary(&run); let conclusion = run.conclusion.as_deref().unwrap_or("success"); if conclusion == "success" { return Ok(()); From 91f5e74d0da5ad5d3bbd82e0bff1d9656ddf4878 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Tue, 11 Aug 2026 17:17:18 -0400 Subject: [PATCH 2/2] Print gh api workflow watch errors --- tools/ci/src/workflow_watch.rs | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/tools/ci/src/workflow_watch.rs b/tools/ci/src/workflow_watch.rs index efdbd208d52..616ea158a0c 100644 --- a/tools/ci/src/workflow_watch.rs +++ b/tools/ci/src/workflow_watch.rs @@ -1,6 +1,6 @@ use anyhow::{bail, Context, Result}; -use duct::cmd; use serde::Deserialize; +use std::process::Command; #[derive(Deserialize)] struct WorkflowRunView { @@ -9,10 +9,34 @@ struct WorkflowRunView { } fn get_workflow_run(repo: &str, run_id: u64) -> Result { - let raw = cmd!("gh", "api", format!("repos/{repo}/actions/runs/{run_id}")) - .read() - .with_context(|| format!("failed to read workflow run {run_id} in {repo}"))?; - serde_json::from_str(&raw).with_context(|| format!("failed to parse workflow run {run_id} in {repo}")) + let path = format!("repos/{repo}/actions/runs/{run_id}"); + let output = Command::new("gh") + .args(["api", "--include", &path]) + .output() + .with_context(|| format!("failed to run gh api for workflow run {run_id} in {repo}"))?; + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + if !output.status.success() { + eprintln!("gh api failed while reading workflow run {run_id} in {repo}:"); + if !stdout.is_empty() { + eprintln!("--- gh api stdout ---\n{stdout}"); + } + if !stderr.is_empty() { + eprintln!("--- gh api stderr ---\n{stderr}"); + } + bail!("gh api {path} exited with {}", output.status); + } + + let body = response_body(&stdout); + serde_json::from_str(body).with_context(|| format!("failed to parse workflow run {run_id} in {repo}")) +} + +fn response_body(response: &str) -> &str { + response + .rsplit_once("\r\n\r\n") + .or_else(|| response.rsplit_once("\n\n")) + .map_or(response, |(_, body)| body) } pub(crate) fn watch_workflow_run(