Skip to content

Commit 36cccc9

Browse files
committed
feat: switch from cowboy to bandit
1 parent 39f4078 commit 36cccc9

File tree

8 files changed

+98
-86
lines changed

8 files changed

+98
-86
lines changed

lib/tableau/components.ex

+19-4
Original file line numberDiff line numberDiff line change
@@ -15,39 +15,54 @@ defmodule Tableau.Components do
1515
:def,
1616
:live_reload,
1717
~g'''
18-
<script defer>
18+
<% {:ok, config} = Tableau.Config.new(Application.get_env(:tableau, :config, %{}) |> Map.new()) %>
19+
<script>
20+
function log(message) {
21+
if (<%= inspect(config.reload_log) %>) {
22+
console.log(`[tableau] ${message}`)
23+
}
24+
}
1925
function connect() {
2026
try {
2127
window.socket = new WebSocket('ws://' + location.host + '/ws');
2228
2329
window.socket.onmessage = function(e) {
2430
if (e.data === "reload") {
31+
log("reloading!");
2532
location.reload();
33+
} else if (e.data === "subscribed") {
34+
log("connected and subscribed!");
2635
}
2736
}
2837
2938
window.socket.onopen = () => {
30-
waitForConnection(() => window.socket.send("subscribe"), 300);
39+
waitForConnection(() => {
40+
log("sending 'subscribe' message");
41+
window.socket.send("subscribe")
42+
}
43+
, 300);
3144
};
3245
3346
window.socket.onclose = () => {
3447
setTimeout(() => connect(), 500);
3548
};
3649
3750
function waitForConnection(callback, interval) {
38-
console.log("Waiting for connection!")
51+
log("waiting for connection!")
3952
if (window.socket.readyState === 1) {
4053
callback();
4154
} else {
42-
console.log("setting a timeout")
55+
log("setting a timeout")
4356
setTimeout(() => waitForConnection(callback, interval), interval);
4457
}
4558
}
4659
} catch (e) {
60+
log(e);
4761
setTimeout(() => connect(), 500);
4862
}
4963
}
5064
65+
log("about to connect");
5166
connect();
5267
</script>
5368
'''html,

lib/tableau/config.ex

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ defmodule Tableau.Config do
99

1010
import Schematic
1111

12-
defstruct [:url, include_dir: "extra", timezone: "Etc/UTC"]
12+
defstruct [:url, include_dir: "extra", timezone: "Etc/UTC", reload_log: false]
1313

1414
def new(config) do
1515
unify(schematic(), config)
@@ -21,6 +21,7 @@ defmodule Tableau.Config do
2121
%{
2222
optional(:include_dir) => str(),
2323
optional(:timezone) => str(),
24+
optional(:reload_log) => bool(),
2425
url: str()
2526
},
2627
convert: false

lib/tableau/extensions/post_extension.ex

+50-42
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ defmodule Tableau.PostExtension do
7676
---
7777
```
7878
"""
79+
7980
{:ok, config} =
8081
Tableau.PostExtension.Config.new(
8182
Map.new(Application.compile_env(:tableau, Tableau.PostExtension, %{}))
@@ -86,50 +87,57 @@ defmodule Tableau.PostExtension do
8687
use Tableau.Extension, key: :posts, type: :pre_build, priority: 100
8788

8889
def run(token) do
89-
Module.create(
90-
Tableau.PostExtension.Posts,
91-
quote do
92-
use NimblePublisher,
93-
build: __MODULE__.Post,
94-
from: "#{unquote(@config.dir)}/*.md",
95-
as: :posts,
96-
highlighters: [:makeup_elixir],
97-
parser: Tableau.PostExtension.Posts.Post
98-
99-
def posts(_opts \\ []) do
100-
@posts
101-
|> Enum.sort_by(& &1.date, {:desc, DateTime})
102-
|> then(fn posts ->
103-
if unquote(@config.future) do
104-
posts
105-
else
106-
Enum.reject(posts, &(DateTime.compare(&1.date, DateTime.utc_now()) == :gt))
90+
:global.trans(
91+
{:create_posts_module, make_ref()},
92+
fn ->
93+
Module.create(
94+
Tableau.PostExtension.Posts,
95+
quote do
96+
use NimblePublisher,
97+
build: __MODULE__.Post,
98+
from: "#{unquote(@config.dir)}/*.md",
99+
as: :posts,
100+
highlighters: [:makeup_elixir],
101+
parser: Tableau.PostExtension.Posts.Post
102+
103+
def posts(_opts \\ []) do
104+
@posts
105+
|> Enum.sort_by(& &1.date, {:desc, DateTime})
106+
|> then(fn posts ->
107+
if unquote(@config.future) do
108+
posts
109+
else
110+
Enum.reject(posts, &(DateTime.compare(&1.date, DateTime.utc_now()) == :gt))
111+
end
112+
end)
107113
end
108-
end)
109-
end
114+
end,
115+
Macro.Env.location(__ENV__)
116+
)
117+
118+
posts =
119+
for post <- apply(Tableau.PostExtension.Posts, :posts, []) do
120+
{:module, _module, _binary, _term} =
121+
Module.create(
122+
Module.concat([post.id]),
123+
quote do
124+
@external_resource unquote(post.file)
125+
use Tableau.Page, unquote(Macro.escape(Keyword.new(post)))
126+
127+
def template(_assigns) do
128+
unquote(post.body)
129+
end
130+
end,
131+
Macro.Env.location(__ENV__)
132+
)
133+
134+
post
135+
end
136+
137+
{:ok, Map.put(token, :posts, posts)}
110138
end,
111-
Macro.Env.location(__ENV__)
139+
[Node.self()],
140+
:infinity
112141
)
113-
114-
posts =
115-
for post <- apply(Tableau.PostExtension.Posts, :posts, []) do
116-
{:module, _module, _binary, _term} =
117-
Module.create(
118-
Module.concat([post.id]),
119-
quote do
120-
@external_resource unquote(post.file)
121-
use Tableau.Page, unquote(Macro.escape(Keyword.new(post)))
122-
123-
def template(_assigns) do
124-
unquote(post.body)
125-
end
126-
end,
127-
Macro.Env.location(__ENV__)
128-
)
129-
130-
post
131-
end
132-
133-
{:ok, Map.put(token, :posts, posts)}
134142
end
135143
end

lib/tableau/router.ex

+7
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,15 @@ defmodule Tableau.Router do
1818
plug :match
1919
plug :dispatch
2020

21+
get "/ws/index.html" do
22+
conn
23+
|> WebSockAdapter.upgrade(Tableau.Websocket, [], timeout: 60_000)
24+
|> halt()
25+
end
26+
2127
match _ do
2228
Logger.error("File not found: #{conn.request_path}")
29+
dbg conn
2330

2431
send_resp(conn, 404, @not_found)
2532
end

lib/tableau/server.ex

+1-12
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,8 @@ defmodule Tableau.Server do
22
@moduledoc false
33
def child_spec(_) do
44
Supervisor.child_spec(
5-
{Plug.Cowboy,
6-
scheme: :http, plug: Tableau.Router, options: [dispatch: dispatch(), port: 4999]},
5+
{Bandit, scheme: :http, plug: Tableau.Router, port: 4999},
76
[]
87
)
98
end
10-
11-
defp dispatch() do
12-
[
13-
{:_,
14-
[
15-
{"/ws", Tableau.Websocket, []},
16-
{:_, Plug.Cowboy.Handler, {Tableau.Router, []}}
17-
]}
18-
]
19-
end
209
end

lib/tableau/websocket.ex

+8-18
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,29 @@
11
defmodule Tableau.Websocket do
22
@moduledoc false
3-
@behaviour :cowboy_websocket
43
@reloader_opts Application.compile_env(:tableau, :reloader, patterns: [])
54

65
require Logger
76

8-
def init(request, state) do
9-
{:cowboy_websocket, request, state}
10-
end
11-
12-
def websocket_init(state) do
7+
def init(_args) do
138
:ok = Tableau.LiveReload.init(name: :tableau_file_watcher)
14-
15-
{:ok, state}
16-
end
17-
18-
def websocket_handle({:text, _text}, state) do
19-
{:reply, {:text, "pong"}, state}
9+
{:ok, []}
2010
end
2111

22-
def websocket_handle({:ping, _text}, state) do
23-
{:reply, {:pong, "PONG"}, state}
12+
def handle_in({"subscribe", [opcode: :text]}, state) do
13+
{:push, {:text, "subscribed"}, state}
2414
end
2515

26-
def websocket_info({:reload, _asset_type}, state) do
27-
{:reply, {:text, "reload"}, state}
16+
def handle_info({:reload, _asset_type}, state) do
17+
{:push, {:text, "reload"}, state}
2818
end
2919

30-
def websocket_info({:file_event, _watcher_pid, {_path, _event}} = file_event, state) do
20+
def handle_info({:file_event, _watcher_pid, {_path, _event}} = file_event, state) do
3121
Tableau.LiveReload.reload!(file_event, patterns: @reloader_opts[:patterns])
3222

3323
{:ok, state}
3424
end
3525

36-
def websocket_info(message, state) do
26+
def handle_info(message, state) do
3727
Logger.warning("Unhandled message: #{inspect(message)}")
3828

3929
{:ok, state}

mix.exs

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,16 @@ defmodule Tableau.MixProject do
3535
{:ex_doc, ">= 0.0.0", only: :dev},
3636
{:file_system, "~> 0.2"},
3737
{:libgraph, "~> 0.16.0"},
38-
{:plug_cowboy, "~> 2.0"},
38+
{:bandit, "~> 1.0"},
39+
{:websock_adapter, "~> 0.5"},
3940
{:plug_static_index_html, "~> 1.0"},
4041
{:schematic, "~> 0.3.1"},
4142
{:nimble_publisher, "~> 1.0"},
4243
{:yaml_elixir, "~> 2.9"},
4344
{:makeup_elixir, ">= 0.0.0"},
4445
{:tz, "~> 0.26.2"},
4546

47+
4648
# {:yaml_front_matter, "~> 1.0"},
4749
# {:jason, "~> 1.4"},
4850
# {:req, "~> 0.3", only: :test},

mix.lock

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
%{
2-
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
3-
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
4-
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
2+
"bandit": {:hex, :bandit, "1.0.0", "2bd87bbf713d0eed0090f2fa162cd1676198122e6c2b68a201c706e354a6d5e5", [:mix], [{:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "32acf6ac030fee1f99fd9c3fcf81671911ae8637e0a61c98111861b466efafdb"},
53
"earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"},
64
"earmark_parser": {:hex, :earmark_parser, "1.4.32", "fa739a0ecfa34493de19426681b23f6814573faee95dfd4b4aafe15a7b5b32c6", [:mix], [], "hexpm", "b8b0dd77d60373e77a3d7e8afa598f325e49e8663a51bcc2b88ef41838cca755"},
75
"ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"},
86
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
97
"floki": {:hex, :floki, "0.34.3", "5e2dcaec5d7c228ce5b1d3501502e308b2d79eb655e4191751a1fe491c37feac", [:mix], [], "hexpm", "9577440eea5b97924b4bf3c7ea55f7b8b6dce589f9b28b096cc294a8dc342341"},
8+
"hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
109
"libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"},
1110
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
1211
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
1312
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
14-
"mime": {:hex, :mime, "2.0.2", "0b9e1a4c840eafb68d820b0e2158ef5c49385d17fb36855ac6e7e087d4b1dcc5", [:mix], [], "hexpm", "e6a3f76b4c277739e36c2e21a2c640778ba4c3846189d5ab19f97f126df5f9b7"},
13+
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
1514
"nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"},
1615
"nimble_publisher": {:hex, :nimble_publisher, "1.0.0", "e7c6b1dc0505a87dcb12e47427bce9f659eba36ab6f0afb8267d21f2ac701871", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "959ab4d9ff41a33a7269082ccf7e9fb76b840d850c872733dd4201591b6ea6f4"},
17-
"plug": {:hex, :plug, "1.13.6", "187beb6b67c6cec50503e940f0434ea4692b19384d47e5fdfd701e93cadb4cc2", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02b9c6b9955bce92c829f31d6284bf53c591ca63c4fb9ff81dfd0418667a34ff"},
18-
"plug_cowboy": {:hex, :plug_cowboy, "2.5.1", "7cc96ff645158a94cf3ec9744464414f02287f832d6847079adfe0b58761cbd0", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "107d0a5865fa92bcb48e631cc0729ae9ccfa0a9f9a1bd8f01acb513abf1c2d64"},
19-
"plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"},
16+
"plug": {:hex, :plug, "1.15.1", "b7efd81c1a1286f13efb3f769de343236bd8b7d23b4a9f40d3002fc39ad8f74c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "459497bd94d041d98d948054ec6c0b76feacd28eec38b219ca04c0de13c79d30"},
17+
"plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"},
2018
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "79fd4fcf34d110605c26560cbae8f23c603ec4158c08298bd4360fdea90bb5cf"},
21-
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
2219
"schematic": {:hex, :schematic, "0.3.1", "be633c1472959dc0ace22dd0e1f1445b099991fec39f6d6e5273d35ebd217ac4", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "52c419b5c405286e2d0369b9ca472b00b850c59a8b0bdf0dd69172ad4418d5ea"},
2320
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
21+
"thousand_island": {:hex, :thousand_island, "1.0.0", "63fc8807d8607c9d74fa670996897c8c8a1f2022c8c68d024182e45249acd756", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "996320c72ba8f34d7be9b02900622e44341649f24359e0f67643e4dda8f23995"},
2422
"tz": {:hex, :tz, "0.26.2", "a40e4bb223344c6fc7b74dda25df1f26b88a30db23fa6e55de843bd79148ccdb", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:mint, "~> 1.5", [hex: :mint, repo: "hexpm", optional: true]}], "hexpm", "224b0618dd1e032778a094040bc710ef9aff6e2fa8fffc2716299486f27b9e68"},
23+
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
24+
"websock_adapter": {:hex, :websock_adapter, "0.5.4", "7af8408e7ed9d56578539594d1ee7d8461e2dd5c3f57b0f2a5352d610ddde757", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d2c238c79c52cbe223fcdae22ca0bb5007a735b9e933870e241fce66afb4f4ab"},
2525
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
2626
"yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"},
2727
}

0 commit comments

Comments
 (0)