Skip to content

Commit e697ff6

Browse files
authored
fix(cli): reject unrecognized flags instead of treating them as issue ids (#2) (#4)
issue list/take/update all set allow_unknown_args: true so bare tokens (e.g. CRY-1) can be captured as issue ids via result.unknown - Optimus has no type: :array equivalent for positional args. That same bucket also silently swallowed any unrecognized flag (a typo, or a real flag the subcommand just doesn't have, like --mine on issue list), which then got looked up as a literal issue id instead of erroring - a raw %Ash.Error.Unknown{} dump reaching the user. run/3 now filters result.unknown for anything flag-shaped before ever calling the command, returning a clean {:smells_bad, "unrecognized option(s): ..."} (exit 22) instead. Every other subcommand already has this covered by Optimus's own parser (allow_unknown_args: false rejects unknown args before we ever see a parse_result), so this only ever fires for the three subcommands that needed the escape hatch. Per the issue: --mine itself is intentionally not being added - --no-mine already covers it (--mine is the implied default).
1 parent 905c238 commit e697ff6

2 files changed

Lines changed: 52 additions & 3 deletions

File tree

app/lib/linear_cli/cli.ex

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,37 @@ defmodule LinearCli.CLI do
9393
end
9494

9595
defp run(fun, result, halt) do
96-
case fun.(result) do
97-
:ok -> :ok
98-
{:error, error} -> handle_error(error, result.options[:debug], halt)
96+
case reject_unknown_flags(result.unknown) do
97+
:ok ->
98+
case fun.(result) do
99+
:ok -> :ok
100+
{:error, error} -> handle_error(error, result.options[:debug], halt)
101+
end
102+
103+
{:error, error} ->
104+
handle_error(error, result.options[:debug], halt)
105+
end
106+
end
107+
108+
# `issue list`/`take`/`update` all set `allow_unknown_args: true` so bare
109+
# tokens (e.g. `CRY-1`) can be captured as issue ids via `result.unknown`
110+
# rather than a declared positional arg (Optimus has no `type: :array`
111+
# equivalent - see their subcommand specs below). That same bucket also
112+
# silently swallows any *unrecognized flag* (e.g. a typo, or a real flag
113+
# this subcommand just doesn't have, like `--mine` on `issue list` - #2),
114+
# which then gets treated as an issue id to look up instead of erroring
115+
# clearly. Every other subcommand has `allow_unknown_args: false` (the
116+
# default), where Optimus itself already rejects unknown args before we
117+
# ever see a parse_result - so `result.unknown` is only ever non-empty here
118+
# for those three subcommands, and only ever contains genuine bare ids
119+
# once this filters out anything flag-shaped.
120+
defp reject_unknown_flags(unknown_tokens) do
121+
case Enum.filter(unknown_tokens, &String.starts_with?(&1, "-")) do
122+
[] ->
123+
:ok
124+
125+
bad_flags ->
126+
{:error, {:smells_bad, "unrecognized option(s): #{Enum.join(bad_flags, ", ")}"}}
99127
end
100128
end
101129

app/test/linear_cli/cli_test.exs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,27 @@ defmodule LinearCli.CLITest do
131131
refute output =~ "CRY-1"
132132
end
133133

134+
test "issue list --mine gives a clear error instead of being treated as an issue id (#2)" do
135+
# `--mine` isn't (and won't be - `--no-mine` already covers it, `--mine`
136+
# is the implied default) a declared flag on `issue list`. Since this
137+
# subcommand allows unknown args (for bare issue ids), an unrecognized
138+
# `-`-prefixed token used to silently fall into that same bucket and get
139+
# looked up as a literal issue id "--mine" instead of erroring - crashing
140+
# with a raw %Ash.Error.Unknown{} dump. No Req.Test stub needed: the fix
141+
# rejects this before any API call happens.
142+
test_pid = self()
143+
halt = fn code -> send(test_pid, {:halted, code}) end
144+
145+
output =
146+
capture_io(:stderr, fn ->
147+
LinearCli.CLI.main(["issue", "list", "--mine"], halt)
148+
end)
149+
150+
assert_received {:halted, 22}
151+
assert output =~ "unrecognized option(s): --mine"
152+
assert output =~ "This smells bad! Bailing."
153+
end
154+
134155
test "an unknown issue id halts with exit code 66" do
135156
test_pid = self()
136157
halt = fn code -> send(test_pid, {:halted, code}) end

0 commit comments

Comments
 (0)