Guard sources create/update against non-interactive terminals - #155
Merged
Conversation
Extends the both-streams TTY guard that sources delete already uses to sources create (always prompts) and sources update (always ends by prompting for the route folder, whether the target came from an explicit uuid or the interactive picker). Both now fail fast with a clear message under a non-interactive terminal instead of hanging. Closes #148
…secret gap in tests - Assert the distinguishing half of each new TTY-guard message (create/update) instead of the shared generic substring, so a reordered branch can't slip past the tests with misleading advice. - Interpolate CREATE_SUBCOMMAND/UPDATE_SUBCOMMAND in their own messages, matching the delete message's existing pattern. - Add a regression test pinning that 'list' still works over a redirected/non-TTY stream (the primary scripted use case). - Add a regression test documenting that rotate-secret is deliberately left unguarded for now (tracked via @todo + follow-up suggestion), so a future change can't silently widen or narrow that gap without a test noticing. - Note the rotate-secret gap in the README so scripters don't assume it's covered by this change.
Collaborator
Author
Independent code review trailRan the required 3-round Round 1 — flagged 4 items:
Round 2 — re-flagged the
Round 3 — re-flagged the
Unresolved after 3 rounds: none outstanding as code changes — the one recurring item ( Skipped: none beyond the |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Extends the both-streams TTY guard that
sources deletealready uses (added in a prior PR) to two more subcommands that can hang or silently no-op under a pipe/cron job:sources create— always prompts for source type/name/route folder/provider, with no non-interactive escape hatch.sources update— always ends by prompting for the route folder, whether the target was resolved from an explicituuidor picked interactively (nouuidopens a picker first).Both now fail fast with a clear error under a non-interactive terminal (either stream not a TTY) instead of hanging or being silently swallowed by the
ExitPromptErrorabort path.Implementation notes
process.stdin.isTTY && process.stdout.isTTY, computed once inrunSourcesCommand) is unchanged and reused as-is — no duplicated TTY-detection logic.usageErrorFor's single delete-only branch into a smallinteractiveGuardMessageFor(subcommand, skipConfirm)helper so the!isInteractivecondition lives in exactly one place inusageErrorFor, with per-subcommand messaging factored out (rule of three: this is now the third subcommand needing this check).'create'/'update'string literals toCREATE_SUBCOMMAND/UPDATE_SUBCOMMANDconstants (matching the existingLIST_SUBCOMMAND/DELETE_SUBCOMMANDpattern), and used them in both the handler map and the new guard messages.sources rotate-secretis deliberately not guarded by this change — see the@todoinsrc/commands/sources.tsand the follow-up suggestion below. It's out of scope for this issue and left in the same statecreate/updatewere left in by the original delete-only guard.Tests
Added, mirroring
delete's existing non-TTY coverage:createrefuses on a non-TTY stdin and on a redirected stdout (both fail fast, no prompt rendered, no API call).updaterefuses on a non-TTY stdin/redirected stdout with nouuid(picker path) and with an explicituuid(direct route-folder-prompt path), since both end at an unanswerable prompt.list(the primary scripted/piped use case) still works over a non-TTY — the widened!isInteractivecheck must not sweep it in.rotate-secretstill works byuuidover a non-TTY — documents the current, deliberate gap so a future change can't silently narrow or widen it without a test noticing.Full suite: 844 tests passing.
npm run lint:fixandnpm run typecheckboth clean.Independent code review
Ran the required 3-round review loop; findings addressed each round (message specificity, a missing
list-on-non-TTY regression test, a stale/optimistic comment about coverage, constant interpolation consistency). See PR comment for the full trail.Viewable at
src/commands/sources.ts(interactiveGuardMessageFor,usageErrorFor) andtests/commands/sources.test.ts.Closes #148
Follow-up suggestions
Guard sources rotate-secret against non-interactive terminals— with no uuid it opens the same kind of picker as update, and a manual-secret (stripe) provider's password prompt also needs a TTY check insidecollectRotateInputonce the provider is known; both currently hang/no-op the same way delete and now create/update did before their guards (suggested: P3, effort: S, evidence: src/commands/sources.ts rotateSecretCommand/collectRotateInput)