feat: send X-Tower-Idempotency-Key on deploy#303
Conversation
Wire up the deploy command to send an idempotency key so consecutive deploys of unchanged source (e.g. to staging then production) collapse to a single AppVersion server-side instead of creating a new version each time. - Auto-populate the key from the git HEAD SHA when the working tree is clean; omit it on a dirty tree so provenance is never misrepresented. - Add --idempotency-key to override detection (useful for CI building outside a checkout) and --no-idempotency-key to opt out on a clean tree. - Print a hint when the server reuses an existing version so the user understands why no new version was created. - Apply the same git auto-detection to the MCP deploy tool.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
BDD regression tests for the X-Tower-Idempotency-Key deploy behavior: explicit --idempotency-key, --no-idempotency-key opt-out, git auto-detect on a clean tree, header omission on a dirty tree, and the reuse hint on a repeat deploy with the same key. The mock API server now records the idempotency key seen on each deploy and reuses a stored (backdated) version when the same key recurs, exposing both via test-only inspection endpoints.
socksy
left a comment
There was a problem hiding this comment.
Please have a look at the output function comment, but otherwise looks good.
| } | ||
|
|
||
| Some(format!( | ||
| "Reusing version `{}` (deployed {}) — no source changes since key {}.", |
There was a problem hiding this comment.
since this is not actually true, but is rather that the git repository doesn't have any committed changes, we should probably mention "git"
|
|
||
| // Only emit the informational hint in normal mode; in JSON mode a | ||
| // bare line would corrupt the structured output. | ||
| if output::get_output_mode().is_normal() { |
There was a problem hiding this comment.
FWIW I think if you're using this function to work out what to write out, then it's almost certainly a code smell (it's public so that we can check for ctrl-c behaviour differently, but wanting to only print things out if it's not json or mcp mode seems to me like an output.rs specific concern).
Additionally, in this case would it make sense to either re-use output::success, or to print it to stderr with output::write_to_stderr? If not, perhaps a simple function addition to output that writes only informational text to output_mode().is_normal() texts would be better?
| // Auto-detect the idempotency key from git (clean tree HEAD) just like | ||
| // the CLI deploy command does, so repeated deploys of unchanged source | ||
| // collapse to a single AppVersion server-side. | ||
| let idempotency_key = crate::util::git::clean_head_sha(&working_dir); |
There was a problem hiding this comment.
would be good to do a SHA of the SHAs for the idempotency_key when git is dirty. Alternatively, swap the warning around to tell the user that because their git repo is not clean, the whole bundle is being re-uploaded (current way around you'd only know this behaviour exists if you pushed a clean repo, and given we state that even an untracked file is enough to make a repo dirty, some people may never hit upon this behaviour and wonder why tower doesn't support it)
Summary
Wires the
tower deploycommand to send anX-Tower-Idempotency-Keyheader so consecutive deploys of identical source (e.g. tostagingthenproduction) reuse a singleAppVersionserver-side instead of creating a new version each time. The server already honors this header; the CLI just didn't send it.Behavior
X-Tower-Idempotency-Key: <git HEAD SHA>.--idempotency-key <value>wins over auto-detection — useful for CI that builds artifacts outside a git checkout.--no-idempotency-keysuppresses the header even on a clean tree (conflicts with--idempotency-key).created_atpredates this deploy, the CLI prints a line explaining that an existing version was reused (suppressed in JSON mode).The MCP
tower_deploytool gets the same git auto-detection.Implementation notes
util::git::clean_head_sha()shells out togit(no new dependencies); returnsNonewhen not in a repo, the tree is dirty, orgitis unavailable.deploy_from_dir → do_deploy_package → deploy_app_package → upload_file_with_progress, which attaches the header when present.