Skip to content

Commit ff36e25

Browse files
committed
Add new hex.search
1 parent 3403c83 commit ff36e25

File tree

4 files changed

+144
-62
lines changed

4 files changed

+144
-62
lines changed

lib/hex/utils.ex

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -333,16 +333,30 @@ defmodule Hex.Utils do
333333
def open_cmd(path) do
334334
case :os.type() do
335335
{:win32, _} ->
336-
{"cmd", ["/c", "start", path]}
336+
{"cmd", win_cmd_args(path)}
337337

338338
{:unix, :darwin} ->
339-
{"open", [path], []}
339+
{"open", [path]}
340340

341341
{:unix, _} ->
342-
{"xdg-open", [path], []}
342+
cond do
343+
System.find_executable("xdg-open") ->
344+
{"xdg-open", [path]}
345+
346+
# When inside WSL
347+
System.find_executable("cmd.exe") ->
348+
{"cmd.exe", win_cmd_args(path)}
349+
350+
true ->
351+
{"open", [path]}
352+
end
343353
end
344354
end
345355

356+
defp win_cmd_args(path) do
357+
["/c", "start", String.replace(path, "&", "^&")]
358+
end
359+
346360
@doc """
347361
Opens a file or URL with the system's default handler.
348362
@@ -355,12 +369,12 @@ defmodule Hex.Utils do
355369
end
356370

357371
if Mix.env() == :test do
358-
defp system_cmd({cmd, args, options}) do
359-
send(self(), {:hex_system_cmd, cmd, args, options})
372+
defp system_cmd({cmd, args}) do
373+
send(self(), {:hex_system_cmd, cmd, args})
360374
end
361375
else
362-
defp system_cmd({cmd, args, options}) do
363-
System.cmd(cmd, args, options)
376+
defp system_cmd({cmd, args}) do
377+
System.cmd(cmd, args)
364378
end
365379
end
366380
end

lib/mix/tasks/hex.search.ex

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,96 @@
11
defmodule Mix.Tasks.Hex.Search do
22
use Mix.Task
33

4-
@shortdoc "Searches for package names"
4+
@shortdoc "Open and perform searches"
55

66
@moduledoc """
7-
Displays packages matching the given search query.
7+
Open and perform searches.
88
9-
If you are authenticated it will additionally search all organizations you are member of.
9+
When invoked without arguments, it opens up a search page on
10+
https://hexdocs.pm with all of your dependencies selected:
1011
11-
$ mix hex.search PACKAGE
12+
$ mix hex.search
1213
13-
## Command line options
14+
You may also pass command line flags, to execute searches
15+
via the command line, according to the modes below.
16+
17+
## Package search
18+
19+
Specify `--package PACKAGE` to search for a given package.
20+
21+
$ mix hex.search --package PACKAGE
22+
23+
If you are authenticated, it will additionally search all organizations
24+
you are member of.
25+
26+
### Options
1427
1528
* `--organization ORGANIZATION` - Set this for private packages belonging to an organization
1629
1730
"""
1831
@behaviour Hex.Mix.TaskDescription
1932

20-
@switches [organization: :string, all_organizations: :boolean]
33+
@switches [organization: :string, package: :string]
2134

2235
@impl true
2336
def run(args) do
24-
Hex.start()
25-
{opts, args} = OptionParser.parse!(args, strict: @switches)
26-
2737
case args do
28-
[package] ->
29-
search_package(package, opts[:organization])
38+
[] ->
39+
hexdocs_search()
3040

3141
_ ->
32-
Mix.raise("""
33-
Invalid arguments, expected:
34-
35-
mix hex.search PACKAGE
36-
""")
42+
{opts, args} = OptionParser.parse!(args, strict: @switches)
43+
44+
case args do
45+
[package] ->
46+
Mix.shell().error(
47+
"mix hex.search PACKAGE is deprecated, use --package PACKAGE instead"
48+
)
49+
50+
package_search(package, opts[:organization])
51+
52+
_ ->
53+
package = opts[:package]
54+
55+
if is_binary(package) and args == [] do
56+
package_search(package, opts[:organization])
57+
else
58+
Mix.raise("""
59+
Invalid arguments, expected:
60+
61+
mix hex.search
62+
mix hex.search --package PACKAGE
63+
""")
64+
end
65+
end
3766
end
3867
end
3968

4069
@impl true
4170
def tasks() do
4271
[
43-
{"PACKAGE", "Searches for package names"}
72+
{"", "Opens up hexdocs.pm with your dependencies"},
73+
{"--package PACKAGE", "Searches for package names"}
4474
]
4575
end
4676

47-
defp search_package(package, organization) do
77+
defp hexdocs_search() do
78+
Mix.Tasks.Deps.Loadpaths.run(["--no-compile", "--no-listeners"])
79+
Hex.start()
80+
81+
packages =
82+
for {_app, info} <- Mix.Dep.Lock.read(),
83+
%{repo: "hexpm", name: name, version: version} <- [Hex.Utils.lock(info)] do
84+
"#{name}:#{version}"
85+
end
86+
|> Enum.join(",")
87+
|> URI.encode_www_form()
88+
89+
Hex.Utils.system_open("https://hexdocs.pm/?packages=#{packages}&q=")
90+
end
91+
92+
defp package_search(package, organization) do
93+
Hex.start()
4894
auth = Mix.Tasks.Hex.auth_info(:read, auth_inline: false)
4995

5096
Hex.API.Package.search(organization, package, auth)

test/mix/tasks/hex.docs_test.exs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ defmodule Mix.Tasks.Hex.DocsTest do
203203
fetched_msg = "Docs fetched: #{docs_home}/#{org_dir}/#{package}/#{latest_version}"
204204
browser_open_msg = "#{docs_home}/#{org_dir}/#{package}/#{latest_version}/index.html"
205205
assert_received {:mix_shell, :info, [^fetched_msg]}
206-
assert_received {:hex_system_cmd, _cmd, browser_open_cmd, _}
206+
assert_received {:hex_system_cmd, _cmd, browser_open_cmd}
207207
assert Enum.fetch!(browser_open_cmd, -1) == browser_open_msg
208208
end)
209209
end
@@ -221,7 +221,7 @@ defmodule Mix.Tasks.Hex.DocsTest do
221221
fetched_msg = "Docs fetched: #{docs_home}/#{org_dir}/#{package}/#{latest_version}"
222222
browser_open_msg = "#{docs_home}/#{org_dir}/#{package}/#{latest_version}/docs_package.epub"
223223
assert_received {:mix_shell, :info, [^fetched_msg]}
224-
assert_received {:hex_system_cmd, _cmd, browser_open_cmd, _}
224+
assert_received {:hex_system_cmd, _cmd, browser_open_cmd}
225225
assert Enum.fetch!(browser_open_cmd, -1) == browser_open_msg
226226
end)
227227
end
@@ -240,7 +240,7 @@ defmodule Mix.Tasks.Hex.DocsTest do
240240
fetched_msg = "Docs fetched: #{docs_home}/#{org_dir}/#{package}/#{version}"
241241
browser_open_msg = "#{docs_home}/#{org_dir}/#{package}/#{version}/index.html"
242242
assert_received {:mix_shell, :info, [^fetched_msg]}
243-
assert_received {:hex_system_cmd, _cmd, browser_open_cmd, _}
243+
assert_received {:hex_system_cmd, _cmd, browser_open_cmd}
244244
assert Enum.fetch!(browser_open_cmd, -1) == browser_open_msg
245245

246246
Mix.Tasks.Hex.Docs.run(["fetch", package, version])
@@ -265,14 +265,14 @@ defmodule Mix.Tasks.Hex.DocsTest do
265265

266266
Mix.Tasks.Hex.Docs.run(["offline", package, version])
267267
browser_open_msg = "#{docs_home}/#{package}/#{version}/index.html"
268-
assert_received {:hex_system_cmd, _cmd, browser_open_cmd, _}
268+
assert_received {:hex_system_cmd, _cmd, browser_open_cmd}
269269
assert Enum.fetch!(browser_open_cmd, -1) == browser_open_msg
270270
end)
271271
end
272272

273273
test "open docs online" do
274274
Mix.Tasks.Hex.Docs.run(["online", "ecto"])
275-
assert_received {:hex_system_cmd, _cmd, browser_open_cmd, _}
275+
assert_received {:hex_system_cmd, _cmd, browser_open_cmd}
276276
assert Enum.fetch!(browser_open_cmd, -1) == "https://hexdocs.pm/ecto"
277277
end
278278

@@ -283,7 +283,7 @@ defmodule Mix.Tasks.Hex.DocsTest do
283283
in_tmp("docs", fn ->
284284
Mix.Dep.Lock.write(%{docs_package: {:hex, :docs_package, "1.1.1"}})
285285
Mix.Tasks.Hex.Docs.run(["online", "docs_package"])
286-
assert_received {:hex_system_cmd, _cmd, browser_open_cmd, _}
286+
assert_received {:hex_system_cmd, _cmd, browser_open_cmd}
287287
assert Enum.fetch!(browser_open_cmd, -1) == "https://hexdocs.pm/docs_package/1.1.1"
288288
end)
289289
end
@@ -295,7 +295,7 @@ defmodule Mix.Tasks.Hex.DocsTest do
295295
in_tmp("docs", fn ->
296296
Mix.Dep.Lock.write(%{docs_package: {:hex, :docs_package, "1.1.1"}})
297297
Mix.Tasks.Hex.Docs.run(["online", "docs_package", "--latest"])
298-
assert_received {:hex_system_cmd, _cmd, browser_open_cmd, _}
298+
assert_received {:hex_system_cmd, _cmd, browser_open_cmd}
299299
assert Enum.fetch!(browser_open_cmd, -1) == "https://hexdocs.pm/docs_package"
300300
end)
301301
end
@@ -314,7 +314,7 @@ defmodule Mix.Tasks.Hex.DocsTest do
314314
fetched_msg = "Docs fetched: #{docs_home}/hexpm/#{package}/#{version}"
315315
browser_open_msg = "#{docs_home}/hexpm/#{package}/#{version}/index.html"
316316
assert_received {:mix_shell, :info, [^fetched_msg]}
317-
assert_received {:hex_system_cmd, _cmd, browser_open_cmd, _}
317+
assert_received {:hex_system_cmd, _cmd, browser_open_cmd}
318318
assert Enum.fetch!(browser_open_cmd, -1) == browser_open_msg
319319
assert File.exists?("#{docs_home}/hexpm/#{package}/#{version}")
320320
end)

test/mix/tasks/hex.search_test.exs

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,68 @@
11
defmodule Mix.Tasks.Hex.SearchTest do
22
use HexTest.IntegrationCase
33

4-
test "search" do
5-
Mix.Tasks.Hex.Search.run(["doc"])
6-
assert_received {:mix_shell, :info, ["ex_doc" <> ex_doc]}
7-
assert_received {:mix_shell, :info, ["only_doc" <> only_doc]}
8-
assert ex_doc =~ ~r"\w*0\.1\.0.*http://localhost:4043/packages/ex_doc"
9-
assert only_doc =~ ~r"\w*0\.1\.0.*http://localhost:4043/packages/only_doc"
10-
end
4+
describe "hexdocs" do
5+
test "no args" do
6+
Mix.Tasks.Hex.Search.run([])
7+
assert_received {:hex_system_cmd, "open", ["https://hexdocs.pm/?packages=" <> packages]}
118

12-
test "empty search" do
13-
Mix.Tasks.Hex.Search.run(["bloopdoopbloop"])
14-
assert_received {:mix_shell, :info, ["No packages found"]}
9+
vsn = Application.spec(:plug, :vsn)
10+
assert packages =~ URI.encode_www_form("plug:#{vsn},")
11+
assert String.ends_with?(packages, "&q=")
12+
end
1513
end
1614

17-
test "search all private packages" do
18-
in_tmp(fn ->
19-
set_home_tmp()
20-
auth = Hexpm.new_user("searchuser1", "[email protected]", "password", "searchuser1")
21-
Hexpm.new_repo("searchrepo1", auth)
22-
Hex.State.put(:api_key, auth[:key])
15+
describe "package" do
16+
test "backwards compatibility" do
17+
Mix.Tasks.Hex.Search.run(["bloopdoopbloop"])
18+
19+
assert_received {:mix_shell, :error,
20+
["mix hex.search PACKAGE is deprecated, use --package PACKAGE instead"]}
2321

24-
Mix.Tasks.Hex.Search.run(["doc"])
22+
assert_received {:mix_shell, :info, ["No packages found"]}
23+
end
2524

25+
test "no results" do
26+
Mix.Tasks.Hex.Search.run(["--package", "bloopdoopbloop"])
27+
assert_received {:mix_shell, :info, ["No packages found"]}
28+
end
29+
30+
test "public packages" do
31+
Mix.Tasks.Hex.Search.run(["--package", "doc"])
2632
assert_received {:mix_shell, :info, ["ex_doc" <> ex_doc]}
2733
assert_received {:mix_shell, :info, ["only_doc" <> only_doc]}
2834
assert ex_doc =~ ~r"\w*0\.1\.0.*http://localhost:4043/packages/ex_doc"
2935
assert only_doc =~ ~r"\w*0\.1\.0.*http://localhost:4043/packages/only_doc"
30-
end)
31-
end
36+
end
37+
38+
test "all private packages" do
39+
in_tmp(fn ->
40+
set_home_tmp()
41+
auth = Hexpm.new_user("searchuser1", "[email protected]", "password", "searchuser1")
42+
Hexpm.new_repo("searchrepo1", auth)
43+
Hex.State.put(:api_key, auth[:key])
44+
45+
Mix.Tasks.Hex.Search.run(["--package", "doc"])
46+
47+
assert_received {:mix_shell, :info, ["ex_doc" <> ex_doc]}
48+
assert_received {:mix_shell, :info, ["only_doc" <> only_doc]}
49+
assert ex_doc =~ ~r"\w*0\.1\.0.*http://localhost:4043/packages/ex_doc"
50+
assert only_doc =~ ~r"\w*0\.1\.0.*http://localhost:4043/packages/only_doc"
51+
end)
52+
end
3253

33-
test "search private package" do
34-
in_tmp(fn ->
35-
set_home_tmp()
36-
auth = Hexpm.new_user("searchuser2", "[email protected]", "password", "searchuser2")
37-
Hexpm.new_repo("searchrepo2", auth)
38-
Hex.State.put(:api_key, auth[:key])
54+
test "org packages" do
55+
in_tmp(fn ->
56+
set_home_tmp()
57+
auth = Hexpm.new_user("searchuser2", "[email protected]", "password", "searchuser2")
58+
Hexpm.new_repo("searchrepo2", auth)
59+
Hex.State.put(:api_key, auth[:key])
3960

40-
Mix.Tasks.Hex.Search.run(["doc", "--organization", "searchrepo2"])
61+
Mix.Tasks.Hex.Search.run(["--package", "doc", "--organization", "searchrepo2"])
4162

42-
refute_received {:mix_shell, :info, ["ex_doc" <> _]}
43-
refute_received {:mix_shell, :info, ["only_doc" <> _]}
44-
end)
63+
refute_received {:mix_shell, :info, ["ex_doc" <> _]}
64+
refute_received {:mix_shell, :info, ["only_doc" <> _]}
65+
end)
66+
end
4567
end
4668
end

0 commit comments

Comments
 (0)