Skip to content

Commit 33eaf47

Browse files
brandonrcclaude
andcommitted
fix(admin): always send dry_run on storage-GC requests
`run_storage_gc` built its body with `dry_run.then_some(true)`. The generated SDK field is `Option<bool>` carrying `#[serde(skip_serializing_if = "Option::is_none")]`, so on a live run (no `--dry-run`) the `None` dropped the key entirely and the CLI posted `{}` to `POST /api/v1/admin/storage-gc`. That relied on the server defaulting the field. artifact-keeper#3619 removes that default: `dry_run` becomes required and unknown fields are rejected, so `{}` is refused with 422 and no GC runs. `ak admin storage-gc run --dry-run` was always fine — it sent `{"dry_run": true}`. Send the value unconditionally instead, via a small `storage_gc_body` helper so the body is testable without a client. A live run now posts `{"dry_run": false}`, which is what the 1.8.x backend already means by an empty body, so this is safe to merge before #3619 and required after it. The added test asserts both bodies; restoring `then_some(true)` fails it with `left: Object {}`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W6pg381RiaM9GsdVEyrN8M
1 parent 393be2d commit 33eaf47

2 files changed

Lines changed: 32 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- `ak admin storage-gc run` now always sends `dry_run` explicitly, so a live run posts `{"dry_run": false}` instead of an empty body. Companion to artifact-keeper#3619, which makes `dry_run` required on the storage-GC endpoint and rejects unknown fields (422)
13+
1014
## [1.2.0] - 2026-07-13
1115

1216
### Added

src/commands/admin.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3975,13 +3975,23 @@ async fn toggle_ci_oidc_mapping(
39753975

39763976
// ---- Storage GC and reports ----
39773977

3978+
/// Build the `POST /api/v1/admin/storage-gc` request body.
3979+
///
3980+
/// `dry_run` is always sent explicitly. The generated field is
3981+
/// `Option<bool>` with `skip_serializing_if = "Option::is_none"`, so a `None`
3982+
/// drops the key and posts `{}` — which the backend rejects with 422 once
3983+
/// artifact-keeper#3619 lands (`dry_run` required, unknown fields denied).
3984+
fn storage_gc_body(dry_run: bool) -> artifact_keeper_sdk::types::StorageGcRequest {
3985+
artifact_keeper_sdk::types::StorageGcRequest {
3986+
dry_run: Some(dry_run),
3987+
}
3988+
}
3989+
39783990
async fn run_storage_gc(dry_run: bool, global: &GlobalArgs) -> Result<()> {
39793991
let client = client_for(global)?;
39803992
let spinner = output::spinner("Running storage garbage collection...");
39813993

3982-
let body = artifact_keeper_sdk::types::StorageGcRequest {
3983-
dry_run: dry_run.then_some(true),
3984-
};
3994+
let body = storage_gc_body(dry_run);
39853995

39863996
let r = client
39873997
.run_storage_gc()
@@ -7204,6 +7214,21 @@ mod tests {
72047214
}
72057215
}
72067216

7217+
#[test]
7218+
fn storage_gc_body_always_sends_dry_run() {
7219+
// A live run must send `"dry_run": false`, not an empty body: the
7220+
// backend requires the field and rejects unknown ones
7221+
// (artifact-keeper#3619).
7222+
assert_eq!(
7223+
serde_json::to_value(storage_gc_body(false)).unwrap(),
7224+
json!({ "dry_run": false })
7225+
);
7226+
assert_eq!(
7227+
serde_json::to_value(storage_gc_body(true)).unwrap(),
7228+
json!({ "dry_run": true })
7229+
);
7230+
}
7231+
72077232
#[test]
72087233
fn parse_storage_gc_oci_report() {
72097234
let cli = parse(&["test", "storage-gc", "oci-report", "--grace-hours", "48"]);

0 commit comments

Comments
 (0)