feat: Tier 3 bundle - hardening and observability gaps #30

Merged
David merged 2 commits from feat/tier-3-bundle into main 2026-05-14 18:57:31 +02:00
Owner

Summary

Third bundle in the TODO sweep. Builds on feat/tier-2-bundle; merge that one first or this PR's diff will look larger than it is.

  • --dry-run / --quiet everywhere. Every remaining mutation path now honours the global flags: issue comment, set-state, set-estimation, sprint set, tag add/remove, link/unlink, attachment add/remove, work add/delete, and the multi-step daily-sync. Each prints a single DRY-RUN: would ... line through say! and returns before any state-changing request fires, so scripted callers can stage a run end-to-end before flipping the switch. Daily-sync lists each step it would take.
  • -v verbosity ladder. Counted flag (warn/info/debug/trace) wired into the tracing_subscriber env filter; RUST_LOG still wins when set. The dead --color flag is removed.
  • GET retries. yt::client::get_with_retry retries idempotent GETs on transport errors and 5xx responses with a 200ms / 800ms backoff; 4xx fails immediately so auth and permission problems still surface quickly.
  • Local-time yt work check. Today's window now anchors to local-time midnight via OffsetDateTime::now_local() with a UTC fallback for containers without /etc/localtime. Adds the local-offset feature on the time crate.
  • yt update already-up-to-date short-circuit. HEAD the package, parse Last-Modified, compare to the baked YOUTRACK_CLI_BUILD_DATE. Older-or-equal prints a friendly message and exits 0. Unparseable / missing header silently falls through to the download. New --force bypasses. Dry-run now precedes the freshness probe so --dry-run never touches the network.

Test plan

  • cargo fmt --all
  • cargo clippy --all-targets --all-features -- --deny warnings
  • cargo test --all-targets (187 passed)
  • Manual: yt --dry-run issue comment ISSUE-1 "hi" prints DRY-RUN: line and exits 0 without hitting the API
  • Manual: yt -vv config show prints debug-level tracing
  • Manual: yt update against a published binary older than the running build prints "Already up to date" and exits 0; same command with --force re-downloads
## Summary Third bundle in the TODO sweep. Builds on `feat/tier-2-bundle`; merge that one first or this PR's diff will look larger than it is. * **`--dry-run` / `--quiet` everywhere.** Every remaining mutation path now honours the global flags: issue comment, set-state, set-estimation, sprint set, tag add/remove, link/unlink, attachment add/remove, work add/delete, and the multi-step daily-sync. Each prints a single `DRY-RUN: would ...` line through `say!` and returns before any state-changing request fires, so scripted callers can stage a run end-to-end before flipping the switch. Daily-sync lists each step it would take. * **`-v` verbosity ladder.** Counted flag (warn/info/debug/trace) wired into the `tracing_subscriber` env filter; `RUST_LOG` still wins when set. The dead `--color` flag is removed. * **GET retries.** `yt::client::get_with_retry` retries idempotent GETs on transport errors and 5xx responses with a 200ms / 800ms backoff; 4xx fails immediately so auth and permission problems still surface quickly. * **Local-time `yt work check`.** Today's window now anchors to local-time midnight via `OffsetDateTime::now_local()` with a UTC fallback for containers without `/etc/localtime`. Adds the `local-offset` feature on the `time` crate. * **`yt update` already-up-to-date short-circuit.** HEAD the package, parse `Last-Modified`, compare to the baked `YOUTRACK_CLI_BUILD_DATE`. Older-or-equal prints a friendly message and exits 0. Unparseable / missing header silently falls through to the download. New `--force` bypasses. Dry-run now precedes the freshness probe so `--dry-run` never touches the network. ## Test plan - [x] `cargo fmt --all` - [x] `cargo clippy --all-targets --all-features -- --deny warnings` - [x] `cargo test --all-targets` (187 passed) - [ ] Manual: `yt --dry-run issue comment ISSUE-1 "hi"` prints `DRY-RUN:` line and exits 0 without hitting the API - [ ] Manual: `yt -vv config show` prints debug-level tracing - [ ] Manual: `yt update` against a published binary older than the running build prints "Already up to date" and exits 0; same command with `--force` re-downloads
Wires the global `--quiet` and `--dry-run` flags through every remaining mutation path so scripted callers can stage a run end-to-end before flipping the switch. Each mutation now prints a single `DRY-RUN: would ...` line via `say!` and returns Ok(()) before any state-changing request fires. Covers: issue comment, set-state, set-estimation, sprint set, tag add/remove, link/unlink, attachment add/remove, work add/delete, and the multi-step daily-sync (which prints each step it would take).

Adds a `-v` / `--verbose` count flag with a four-step verbosity ladder (warn/info/debug/trace) wired into the `tracing_subscriber` env filter; `RUST_LOG` still wins when set. Removes the dead `--color` flag.

Retries idempotent GETs in `yt::client` on transport failures and 5xx responses with a 200ms / 800ms backoff; 4xx still fails immediately. Production code paths that read from YouTrack benefit transparently; 4xx-fast keeps auth/permission errors quick to surface.

Anchors `yt work check` to local-time midnight via `OffsetDateTime::now_local()` with a graceful fallback to UTC when the local offset cannot be determined (typically containers without `/etc/localtime`). The `time` crate's `local-offset` feature is enabled to make this available.

Adds an already-up-to-date short-circuit to `yt update`: issues a HEAD before downloading, parses the `Last-Modified` header and compares it against the baked `YOUTRACK_CLI_BUILD_DATE`. When the remote build is older or equal the command prints a friendly message and exits 0; an unparseable or missing header silently falls through to the download. A new `--force` flag skips the freshness check entirely. Dry-run now precedes the freshness probe, so `--dry-run` never touches the network.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
David changed target branch from feat/tier-2-bundle to main 2026-05-14 18:57:17 +02:00
Merge branch 'main' into feat/tier-3-bundle
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt + clippy + build + tests (pull_request) Successful in 25s
d83f263888
David merged commit c2756931f5 into main 2026-05-14 18:57:31 +02:00
David deleted branch feat/tier-3-bundle 2026-05-14 18:57:31 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pandoras-box/youtrack-cli!30
No description provided.