Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions app/lib/linear_cli/cli.ex
Original file line number Diff line number Diff line change
Expand Up @@ -770,10 +770,8 @@ defmodule LinearCli.CLI do
],
comment: [
name: "comment",
about: "Add a comment to an issue",
args: [
issue_id: [value_name: "ISSUE_ID", help: "The Issue (i.e. CRY-1)", required: true]
],
about: "Add a comment to one or more issues (ISSUE_ID...)",
allow_unknown_args: true,
options: [
comment: [
short: "-m",
Expand Down
51 changes: 43 additions & 8 deletions app/lib/linear_cli/cli/commands.ex
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ defmodule LinearCli.CLI.Commands do
end

@doc """
Adds a comment to a single issue.
Adds a comment to one or more issues (ISSUE_ID...).

`--comment`/`-m` and `--body-file` are mutually exclusive. `--body-file`
reads the body from a file (`-` for stdin) - the way to supply a large
Expand All @@ -566,6 +566,10 @@ defmodule LinearCli.CLI.Commands do
not protect against. Without either option, `comment_for/2`'s existing
behavior applies (prompt, or open an editor for `-`).

When multiple issue IDs are given, the same comment body is posted to
each concurrently. The interactive prompt (when neither `-m` nor
`--body-file` is given) uses the first issue's context.

Calls `Linear.add_comment/2` directly rather than
`LinearCli.CLI.IssueHelpers.issue_comment/2` so the confirmation can be
suppressed under `--output json` - matching how `print_move_results/3`
Expand All @@ -574,18 +578,49 @@ defmodule LinearCli.CLI.Commands do
New in this port - Ruby has no equivalent.
"""
@spec issue_comment(Optimus.ParseResult.t()) :: :ok | {:error, term()}
def issue_comment(%{args: %{issue_id: issue_id}, options: options}) do
with :ok <- validate_body_file_exclusion(options, :comment, "--comment"),
def issue_comment(%{unknown: issue_ids, options: options}) do
with :ok <- validate_issue_ids(issue_ids),
:ok <- validate_body_file_exclusion(options, :comment, "--comment"),
{:ok, comment_text} <- resolve_body_from_file(options, :comment),
{:ok, [issue]} <- Linear.issues(%{ids: [IssueHelpers.expand_issue_id(issue_id)]}),
body = WhatFor.comment_for(issue, comment_text),
{:ok, comment} <- Linear.add_comment(issue.identifier, body) do
unless options.output == "json", do: Prompt.ok("Comment added to #{issue.identifier}")
Display.show(comment, %{output: options.output})
{:ok, issues} <-
Linear.issues(%{ids: Enum.map(issue_ids, &IssueHelpers.expand_issue_id/1)}),
body = WhatFor.comment_for(hd(issues), comment_text),
{:ok, pairs} <- add_comments_to_issues(issues, body) do
unless options.output == "json" do
Enum.each(pairs, fn {issue, _comment} ->
Prompt.ok("Comment added to #{issue.identifier}")
end)
end

Display.show(one_or_many(Enum.map(pairs, &elem(&1, 1))), %{output: options.output})
:ok
end
end

defp add_comments_to_issues(issues, body) do
issues
|> Task.async_stream(
fn issue ->
case Linear.add_comment(issue.identifier, body) do
{:ok, comment} -> {:ok, {issue, comment}}
{:error, reason} -> {:error, reason}
end
end,
max_concurrency: min(length(issues), @max_concurrent_issue_updates),
ordered: true,
timeout: 30_000
)
|> Enum.reduce_while({:ok, []}, fn
{:ok, {:ok, pair}}, {:ok, acc} -> {:cont, {:ok, [pair | acc]}}
{:ok, {:error, reason}}, _acc -> {:halt, {:error, reason}}
{:exit, reason}, _acc -> {:halt, {:error, {:task_exit, reason}}}
end)
|> then(fn
{:ok, results} -> {:ok, Enum.reverse(results)}
error -> error
end)
end

defp validate_body_file_exclusion(options, text_key, flag_name) do
if not is_nil(Map.get(options, :body_file)) and not is_nil(Map.get(options, text_key)) do
{:error, {:smells_bad, "give #{flag_name} or --body-file, not both"}}
Expand Down
37 changes: 37 additions & 0 deletions app/test/linear_cli/cli/issue_commands_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3775,5 +3775,42 @@ defmodule LinearCli.CLI.IssueCommandsTest do

assert %{"id" => "c1"} = Jason.decode!(output)
end

test "multiple ISSUE_IDs each receive the comment" do
test_pid = self()

stub_lookup_and([
{"commentCreate",
fn _decoded ->
send(test_pid, :comment_created)
comment_created()
end}
])

output =
capture_io(fn ->
assert :ok =
LinearCli.CLI.main(["issue", "comment", "CRY-1", "CRY-2", "-m", "lgtm"])
end)

assert output =~ "Comment added to"
assert_received :comment_created
assert_received :comment_created
end

test "no ISSUE_IDs is a smells_bad error" do
test_pid = self()
halt = fn code -> send(test_pid, {:halted, code}) end

Req.Test.stub(LinearCli.Api, fn _conn -> raise "no GraphQL call should happen" end)

output =
capture_io(:stderr, fn ->
LinearCli.CLI.main(["issue", "comment", "-m", "lgtm"], halt)
end)

assert_received {:halted, 22}
assert output =~ "No issue IDs provided!"
end
end
end
Loading