diff --git a/app/lib/linear_cli/cli.ex b/app/lib/linear_cli/cli.ex index dca9238..0e9699f 100644 --- a/app/lib/linear_cli/cli.ex +++ b/app/lib/linear_cli/cli.ex @@ -282,6 +282,32 @@ defmodule LinearCli.CLI do halt.(66) end + # LinearCli.Api.call/2's {:error, :missing_api_key} (LINEAR_API_KEY not + # set), reached through any of the Ash manual actions that wrap it - + # Ash's own action pipeline stringifies the original reason into + # Ash.Error.Unknown.UnknownError's :error field ("unknown error: + # :missing_api_key", verified directly) rather than preserving the atom, + # hence the exact-string match below instead of `error: :missing_api_key`. + # A plain pattern match (no guard - `=~` isn't guard-safe) degrades + # gracefully to the generic catch-all below if Ash's wrapping format ever + # changes, rather than raising a fresh error of its own. A missing API key + # is a configuration problem, not a surprising crash - give it a clear + # message and sysexits.h's EX_CONFIG (78) instead of the catch-all's raw + # error dump. See #74. + defp handle_error( + %Ash.Error.Unknown{ + errors: [%Ash.Error.Unknown.UnknownError{error: "unknown error: :missing_api_key"} | _] + }, + debug, + halt + ) do + IO.puts(:stderr, "LINEAR_API_KEY is not set.") + IO.puts(:stderr, "Set it to your Linear API key - see https://linear.app/settings/api") + IO.puts(:stderr, "** Missing configuration, cannot continue **") + maybe_print_backtrace(debug) + halt.(78) + end + # Ported from CLI::Caller#call's `rescue SmellsBad` clause. See # `LinearCli.CLI.IssueHelpers`'s moduledoc for where this tagged tuple # comes from. diff --git a/app/test/linear_cli/cli/missing_api_key_test.exs b/app/test/linear_cli/cli/missing_api_key_test.exs new file mode 100644 index 0000000..4526c1f --- /dev/null +++ b/app/test/linear_cli/cli/missing_api_key_test.exs @@ -0,0 +1,23 @@ +defmodule LinearCli.CLI.MissingApiKeyTest do + # async: false - unsets the real (VM-global, not per-process) + # LINEAR_API_KEY env var, same reason/pattern as LinearCli.ApiTest. + use ExUnit.Case, async: false + import ExUnit.CaptureIO + + test "a missing LINEAR_API_KEY gives a clear message and exits 78, not a raw Ash dump" do + previous = System.get_env("LINEAR_API_KEY") + System.delete_env("LINEAR_API_KEY") + on_exit(fn -> previous && System.put_env("LINEAR_API_KEY", previous) end) + + test_pid = self() + halt = fn code -> send(test_pid, {:halted, code}) end + + output = capture_io(:stderr, fn -> LinearCli.CLI.main(["whoami"], halt) end) + + assert_received {:halted, 78} + assert output =~ "LINEAR_API_KEY is not set." + assert output =~ "https://linear.app/settings/api" + refute output =~ "What the heck is this?" + refute output =~ "Ash.Error" + end +end