ref:9dfc133550b6bd004b3b9903431df54fb6d2ea71

fix(cli): size_bytes field, runner object parsing, board response

- release.rs: rename Asset.size -> size_bytes to match server response; update all format_size call sites and upload response parsing to use size_bytes - ci.rs: change CiJob.runner from Option<String> to Option<serde_json::Value> and extract .name from the object (with string fallback) for display - board.rs: handle both issues-array and issue_count-integer column formats; show issue count in header regardless of which format the server returns Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SHA: 9dfc133550b6bd004b3b9903431df54fb6d2ea71
Author: Cole Christensen <cole.christensen@macmillan.com>
Date: 2026-03-21 20:19
Parents: 31f84b1
3 files changed +17 -9
Type
src/commands/board.rs +8 −3
@@ -54,12 +54,13 @@
for col in &columns {
let col_name = col.get("name").and_then(|v| v.as_str()).unwrap_or("?");
let issues = col
let issue_count = col
.get("issues")
.and_then(|v| v.as_array())
.map(|a| a.len())
.map(|a| a.len() as u64)
.or_else(|| col.get("issue_count").and_then(|v| v.as_u64()))
.unwrap_or(0);
output::header(&format!("{} ({})", col_name, issue_count));
output::header(&format!("{} ({})", col_name, issues));
if let Some(issue_list) = col.get("issues").and_then(|v| v.as_array()) {
for issue in issue_list {
@@ -69,6 +70,10 @@
.unwrap_or(0);
let title = issue.get("title").and_then(|v| v.as_str()).unwrap_or("?");
output::detail(&format!("#{}", num), title);
}
} else if let Some(count) = col.get("issue_count").and_then(|v| v.as_u64()) {
if count > 0 {
output::detail(" ", &format!("{} issue(s)", count));
}
}
}
src/commands/ci.rs +5 −2
@@ -121,6 +121,6 @@
started_at: Option<String>,
completed_at: Option<String>,
duration_seconds: Option<f64>,
runner: Option<String>,
runner: Option<serde_json::Value>,
exit_code: Option<i32>,
}
@@ -342,7 +342,10 @@
output::detail("Duration", &format!("{:.1}s", d));
}
if let Some(ref runner) = job.runner {
let name = runner.get("name").and_then(|v| v.as_str())
.or_else(|| runner.as_str())
output::detail("Runner", runner);
.unwrap_or("?");
output::detail("Runner", name);
}
if let Some(code) = job.exit_code {
output::detail("Exit Code", &code.to_string());
src/commands/release.rs +4 −4
@@ -170,7 +170,7 @@
#[serde(default)]
filename: Option<String>,
#[serde(default)]
size: Option<u64>,
size_bytes: Option<u64>,
#[serde(default)]
download_count: Option<u64>,
#[serde(default)]
@@ -345,7 +345,7 @@
.map(|a| {
vec![
a.filename.clone().unwrap_or_default(),
format_size(a.size.unwrap_or(0)),
format_size(a.size_bytes.unwrap_or(0)),
a.download_count.unwrap_or(0).to_string(),
a.short_id.clone().unwrap_or_default(),
]
@@ -510,6 +510,6 @@
let asset_size = resp
.get("asset")
.or_else(|| resp.get("data"))
.and_then(|a| a.get("size"))
.and_then(|a| a.get("size_bytes"))
.and_then(|s| s.as_u64())
.unwrap_or(0);
@@ -597,7 +597,7 @@
.map(|a| {
vec![
a.filename.clone().unwrap_or_default(),
format_size(a.size.unwrap_or(0)),
format_size(a.size_bytes.unwrap_or(0)),
a.download_count.unwrap_or(0).to_string(),
a.short_id.clone().unwrap_or_default(),
]