feat(cli): implement device revoke

- Remove device from devices.json
- Append to revoked.json with timestamp and revoked_by
- Delete Gitea deploy key (best-effort, warns if env vars missing)
- Always commit both devices.json and revoked.json together
- Print revoked signing public key for audit confirmation
- Guard against revoking the current device (would lose push access)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-05-02 12:22:59 -04:00
parent b1f9f2fbfc
commit 15d691abb2

View File

@@ -2482,13 +2482,13 @@ fn cmd_device(action: DeviceAction) -> Result<()> {
} }
} }
// Commit devices.json + revoked.json. // Commit devices.json + revoked.json (always both — revoked.json
let mut paths = vec![".relicario/devices.json"]; // was just written above so it is guaranteed to exist).
if revoked_path.exists() { let add_args = [
paths.push(".relicario/revoked.json"); "add",
} ".relicario/devices.json",
let mut add_args = vec!["add"]; ".relicario/revoked.json",
add_args.extend_from_slice(&paths); ];
let status = crate::helpers::git_command(&root, &add_args).status()?; let status = crate::helpers::git_command(&root, &add_args).status()?;
if !status.success() { if !status.success() {
anyhow::bail!("git add failed"); anyhow::bail!("git add failed");
@@ -2501,6 +2501,7 @@ fn cmd_device(action: DeviceAction) -> Result<()> {
} }
eprintln!("Device '{}' revoked.", name); eprintln!("Device '{}' revoked.", name);
eprintln!("Revoked signing key: {}", device.public_key);
Ok(()) Ok(())
} }