This commit is contained in:
mithereal 2022-09-17 12:09:04 -07:00
parent 03e46f81f8
commit 84caea3af2
17 changed files with 402 additions and 140 deletions

209
.credo.exs Normal file
View File

@ -0,0 +1,209 @@
# This file contains the configuration for Credo and you are probably reading
# this after creating it with `mix credo.gen.config`.
#
# If you find anything wrong or unclear in this file, please report an
# issue on GitHub: https://github.com/rrrene/credo/issues
#
%{
#
# You can have as many configs as you like in the `configs:` field.
configs: [
%{
#
# Run any config using `mix credo -C <name>`. If no config name is given
# "default" is used.
#
name: "default",
#
# These are the files included in the analysis:
files: %{
#
# You can give explicit globs or simply directories.
# In the latter case `**/*.{ex,exs}` will be used.
#
included: [
"lib/",
"src/",
"test/",
"web/",
"apps/*/lib/",
"apps/*/src/",
"apps/*/test/",
"apps/*/web/"
],
excluded: [
~r"/_build/",
~r"/deps/",
~r"/node_modules/",
~r"/package-lock.json",
~r"/package.json"
]
},
#
# Load and configure plugins here:
#
plugins: [],
#
# If you create your own checks, you must specify the source files for
# them here, so they can be loaded by Credo before running the analysis.
#
requires: [],
#
# If you want to enforce a style guide and need a more traditional linting
# experience, you can change `strict` to `true` below:
#
strict: false,
#
# To modify the timeout for parsing files, change this value:
#
parse_timeout: 120_000,
#
# If you want to use uncolored output by default, you can change `color`
# to `false` below:
#
color: true,
#
# You can customize the parameters of any check by adding a second element
# to the tuple.
#
# To disable a check put `false` as second element:
#
# {Credo.Check.Design.DuplicatedCode, false}
#
checks: %{
enabled: [
#
## Consistency Checks
#
{Credo.Check.Consistency.ExceptionNames, []},
{Credo.Check.Consistency.LineEndings, []},
{Credo.Check.Consistency.ParameterPatternMatching, []},
{Credo.Check.Consistency.SpaceAroundOperators, []},
{Credo.Check.Consistency.SpaceInParentheses, []},
{Credo.Check.Consistency.TabsOrSpaces, []},
#
## Design Checks
#
# You can customize the priority of any check
# Priority values are: `low, normal, high, higher`
#
{Credo.Check.Design.AliasUsage,
[priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]},
# You can also customize the exit_status of each check.
# set this value to 0 (zero).
#
{Credo.Check.Design.TagFIXME, []},
#
## Readability Checks
#
{Credo.Check.Readability.AliasOrder, []},
{Credo.Check.Readability.FunctionNames, []},
{Credo.Check.Readability.LargeNumbers, []},
{Credo.Check.Readability.ModuleAttributeNames, []},
{Credo.Check.Readability.ModuleDoc, []},
{Credo.Check.Readability.ModuleNames, []},
{Credo.Check.Readability.ParenthesesInCondition, []},
{Credo.Check.Readability.ParenthesesOnZeroArityDefs, []},
{Credo.Check.Readability.PipeIntoAnonymousFunctions, []},
{Credo.Check.Readability.PredicateFunctionNames, []},
{Credo.Check.Readability.PreferImplicitTry, []},
{Credo.Check.Readability.RedundantBlankLines, []},
{Credo.Check.Readability.Semicolons, []},
{Credo.Check.Readability.SpaceAfterCommas, []},
{Credo.Check.Readability.StrictModuleLayout, []},
{Credo.Check.Readability.StringSigils, []},
{Credo.Check.Readability.TrailingBlankLine, []},
{Credo.Check.Readability.TrailingWhiteSpace, []},
{Credo.Check.Readability.UnnecessaryAliasExpansion, []},
{Credo.Check.Readability.VariableNames, []},
{Credo.Check.Readability.WithSingleClause, []},
#
## Refactoring Opportunities
#
{Credo.Check.Refactor.Apply, []},
# {Credo.Check.Refactor.CondStatements, []},
# {Credo.Check.Refactor.CyclomaticComplexity, []},
{Credo.Check.Refactor.FunctionArity, []},
# should be activated after elixir version upgrade
{Credo.Check.Refactor.MapInto, false},
{Credo.Check.Refactor.MatchInCondition, []},
{Credo.Check.Refactor.NegatedConditionsInUnless, []},
{Credo.Check.Refactor.NegatedConditionsWithElse, []},
# {Credo.Check.Refactor.Nesting, []},
{Credo.Check.Refactor.UnlessWithElse, []},
{Credo.Check.Refactor.WithClauses, []},
{Credo.Check.Refactor.RedundantWithClauseResult, []},
{Credo.Check.Refactor.FilterFilter, []},
#
## Warnings
#
{Credo.Check.Warning.BoolOperationOnSameValues, []},
{Credo.Check.Warning.ExpensiveEmptyEnumCheck, []},
{Credo.Check.Warning.IExPry, []},
{Credo.Check.Warning.IoInspect, []},
# should be activated after elixir version upgrade
{Credo.Check.Warning.LazyLogging, false},
{Credo.Check.Warning.OperationOnSameValues, []},
{Credo.Check.Warning.OperationWithConstantResult, []},
{Credo.Check.Warning.RaiseInsideRescue, []},
{Credo.Check.Warning.SpecWithStruct, []},
{Credo.Check.Warning.WrongTestFileExtension, []},
{Credo.Check.Warning.UnusedEnumOperation, []},
{Credo.Check.Warning.UnusedFileOperation, []},
{Credo.Check.Warning.UnusedKeywordOperation, []},
{Credo.Check.Warning.UnusedListOperation, []},
{Credo.Check.Warning.UnusedPathOperation, []},
{Credo.Check.Warning.UnusedRegexOperation, []},
{Credo.Check.Warning.UnusedStringOperation, []},
{Credo.Check.Warning.UnusedTupleOperation, []},
{Credo.Check.Warning.UnsafeExec, []}
#
# Checks scheduled for next check update (opt-in for now, just replace `false` with `[]`)
#
# Controversial and experimental checks (opt-in, just replace `false` with `[]`)
#
],
disabled: [
{Credo.Check.Design.TagTODO, false},
{Credo.Check.Consistency.MultiAliasImportRequireUse, false},
{Credo.Check.Consistency.UnusedVariableNames, false},
{Credo.Check.Design.DuplicatedCode, false},
{Credo.Check.Readability.AliasAs, false},
{Credo.Check.Readability.BlockPipe, false},
{Credo.Check.Readability.ImplTrue, false},
{Credo.Check.Readability.MaxLineLength, false},
{Credo.Check.Readability.MultiAlias, false},
{Credo.Check.Readability.SeparateAliasRequire, false},
{Credo.Check.Readability.SinglePipe, false},
{Credo.Check.Readability.Specs, false},
{Credo.Check.Readability.StrictModuleLayout, false},
{Credo.Check.Readability.WithCustomTaggedTuple, false},
{Credo.Check.Refactor.ABCSize, false},
{Credo.Check.Refactor.AppendSingleItem, false},
{Credo.Check.Refactor.DoubleBooleanNegation, false},
{Credo.Check.Refactor.LongQuoteBlocks, false},
{Credo.Check.Refactor.MapJoin, false},
{Credo.Check.Refactor.ModuleDependencies, false},
{Credo.Check.Refactor.NegatedIsNil, false},
{Credo.Check.Refactor.PipeChainStart, false},
{Credo.Check.Refactor.RejectReject, false},
{Credo.Check.Refactor.VariableRebinding, false},
{Credo.Check.Warning.LeakyEnvironment, false},
{Credo.Check.Warning.MapGetUnsafePass, false},
{Credo.Check.Warning.MixEnv, false},
{Credo.Check.Warning.UnsafeToAtom, false},
{Credo.Check.Warning.ApplicationConfigInModuleAttribute, false}
]
#
# Custom checks can be created using `mix credo.gen.check`.
#
}
}
]
}

View File

@ -1,8 +1,6 @@
import Config
config :farside,
update_file: ".update-results",
service_prefix: "service-",
index: "index.eex",
route: "route.eex",
headers: [

View File

@ -1,9 +1,13 @@
defmodule Farside do
@service_prefix Application.fetch_env!(:farside, :service_prefix)
@moduledoc """
Farside
main application functions
This is where we define relation between available services and their parent service.
This enables Farside to redirect with links such as:
farside.link/https://www.youtube.com/watch?v=dQw4w9WgXcQ
"""
# Define relation between available services and their parent service.
# This enables Farside to redirect with links such as:
# farside.link/https://www.youtube.com/watch?v=dQw4w9WgXcQ
@youtube_regex ~r/youtu(.be|be.com)|invidious|piped/
@reddit_regex ~r/reddit.com|libreddit|teddit/
@instagram_regex ~r/instagram.com|bibliogram/
@ -35,42 +39,40 @@ defmodule Farside do
alias Farside.LastUpdated
def get_services_map do
services_map =
Farside.Instance.Supervisor.list()
|> Enum.map(fn service ->
data = :ets.lookup(String.to_atom(service), :default) |> List.first()
Farside.Instance.Supervisor.list()
|> Enum.map(fn service ->
data = :ets.lookup(String.to_atom(service), :default) |> List.first()
instances =
case is_nil(data) do
true ->
[]
case is_nil(data) do
true ->
[]
false ->
{_, service} = data
false ->
{_, service} = data
registry = "#{service.type}_healthy"
registry = "#{service.type}_healthy"
instances =
for instance <- service.instances do
matches = Registry.match(:status, registry, instance)
instances =
for instance <- service.instances do
matches = Registry.match(:status, registry, instance)
{_, instance} =
case Enum.count(matches) > 0 do
true -> List.first(matches)
false -> {:error, nil}
end
instance
{_, instance} =
case Enum.count(matches) > 0 do
true -> List.first(matches)
false -> {:error, nil}
end
|> Enum.reject(fn x -> x == nil end)
Map.put(
service,
:instances,
instances
)
end
end)
instance
end
|> Enum.reject(fn x -> x == nil end)
Map.put(
service,
:instances,
instances
)
end
end)
end
def get_service(service) do
@ -149,18 +151,4 @@ defmodule Farside do
instance
end
end
def save_results(file, data) do
if System.get_env("MIX_ENV") == "dev" do
{:ok, file} = File.open(file, [:append])
bin = :erlang.term_to_binary(data)
IO.binwrite(file, bin)
File.close(file)
end
end
def restore_term(file) do
{:ok, bin} = File.read(file)
:erlang.binary_to_term(bin)
end
end

View File

@ -78,7 +78,18 @@ defmodule Farside.Application do
struct(%Service{}, service_atom)
|> Farside.Instance.Supervisor.start()
|> HealthyCheck.load()
end
response
|> maybe_run()
end
def maybe_run(response) do
if is_nil(System.get_env("FARSIDE_TEST")) do
Task.start(fn ->
Process.sleep(10_000)
UnHealthyCheck.run()
end)
end
response

View File

@ -5,6 +5,8 @@ defmodule Farside.Server.DeadCheck do
use Task
alias Farside.LastUpdated
require Logger
def child_spec(args) do
%{
id: __MODULE__,
@ -20,7 +22,7 @@ defmodule Farside.Server.DeadCheck do
def poll() do
receive do
after
1_200_000 ->
86_400_000 ->
run()
poll()
end
@ -29,6 +31,8 @@ defmodule Farside.Server.DeadCheck do
def run() do
LastUpdated.value(DateTime.utc_now())
Logger.info("Dead Service Check Running")
Registry.dispatch(:status, "dead", fn entries ->
for {pid, _} <- entries, do: GenServer.cast(pid, :check)
end)

View File

@ -5,6 +5,8 @@ defmodule Farside.Server.HealthyCheck do
use Task
alias Farside.LastUpdated
require Logger
def child_spec(args) do
%{
id: __MODULE__,
@ -20,27 +22,19 @@ defmodule Farside.Server.HealthyCheck do
def poll() do
receive do
after
90_000 ->
300_000 ->
run()
poll()
end
end
def load(params) do
Registry.dispatch(:status, "healthy", fn entries ->
for {pid, url} <- entries do
GenServer.cast(pid, :check)
end
end)
params
end
def run() do
LastUpdated.value(DateTime.utc_now())
Logger.info("Healthy Service Check Running")
Registry.dispatch(:status, "healthy", fn entries ->
for {pid, url} <- entries do
for {pid, _url} <- entries do
GenServer.cast(pid, :check)
end
end)

View File

@ -1,6 +1,11 @@
defmodule Farside.Http do
require Logger
@moduledoc """
Http
the http client
"""
@headers Application.fetch_env!(:farside, :headers)
@queries Application.fetch_env!(:farside, :queries)
@recv_timeout String.to_integer(Application.fetch_env!(:farside, :recv_timeout))
@ -108,11 +113,11 @@ defmodule Farside.Http do
nil
end
unless is_nil(data) do
if is_nil(data) do
:bad
else
{_test_url, value, _service} = data
value
else
:bad
end
end
end

View File

@ -1,9 +1,12 @@
defmodule Farside.Instance do
use GenServer
require Logger
@moduledoc """
Instance
this will store the pointer to ets
"""
alias Farside.Http
require Logger
@registry_name :instance
@ -15,6 +18,7 @@ defmodule Farside.Instance do
}
end
@impl true
def init(init_arg) do
ref =
:ets.new(String.to_atom(init_arg.type), [
@ -40,6 +44,7 @@ defmodule Farside.Instance do
GenServer.call(__MODULE__, :shutdown)
end
@impl true
def handle_call(
:shutdown,
_from,
@ -48,6 +53,7 @@ defmodule Farside.Instance do
{:stop, {:ok, "Normal Shutdown"}, state}
end
@impl true
def handle_cast(
:shutdown,
state
@ -61,7 +67,7 @@ defmodule Farside.Instance do
end
@impl true
def handle_info({:DOWN, ref, :process, _pid, _reason}, {names, refs}) do
def handle_info({:DOWN, _ref, :process, _pid, _reason}, {names, refs}) do
:ets.delete(names)
{:noreply, {names, refs}}
end

View File

@ -1,7 +1,11 @@
defmodule Farside.Instance.Supervisor do
use DynamicSupervisor
alias __MODULE__, as: SUPERVISOR
@moduledoc """
Instance Supervisor
this will supervise the instance
"""
alias Farside.Instance, as: SERVER
@name :instance_supervisor

View File

@ -1,6 +1,8 @@
defmodule Farside.LastUpdated do
use Agent
@moduledoc nil
def start_link(initial_value) do
Agent.start_link(fn -> initial_value end, name: __MODULE__)
end

View File

@ -1,6 +1,11 @@
defmodule Farside.Service do
use GenServer
@moduledoc """
Service
this will store the service state
"""
require Logger
alias Farside.Http
@ -21,19 +26,22 @@ defmodule Farside.Service do
}
end
@impl true
def init(data) do
initial_state = %__MODULE__{
url: data.url,
type: data.type,
test_url: data.test_url,
last_update: nil,
last_update:
DateTime.utc_now()
|> DateTime.add(-86_400, :second),
status: []
}
healthy = "#{data.type}_healthy"
unhealthy = "#{data.type}_unhealthy"
Registry.register(:status, healthy, data.url)
Registry.register(:status, "healthy", data.url)
Registry.register(:status, unhealthy, data.url)
Registry.register(:status, "unhealthy", data.url)
{:ok, initial_state}
end
@ -46,6 +54,7 @@ defmodule Farside.Service do
GenServer.call(__MODULE__, :shutdown)
end
@impl true
def handle_call(
:shutdown,
_from,
@ -54,28 +63,6 @@ defmodule Farside.Service do
{:stop, {:ok, "Normal Shutdown"}, state}
end
def handle_cast(
:shutdown,
state
) do
{:stop, :normal, state}
end
@doc false
def via_tuple(id, registry \\ @registry_name) do
{:via, Registry, {registry, id}}
end
@impl true
def handle_info({:DOWN, ref, :process, _pid, _reason}, data) do
{:noreply, data}
end
@impl true
def handle_info(_msg, state) do
{:noreply, state}
end
@impl true
def handle_cast(:load, state) do
reply = Http.test_service(state)
@ -91,57 +78,90 @@ defmodule Farside.Service do
@impl true
def handle_cast(:check, state) do
reply = Http.test_service(state)
status = state.status ++ [reply]
max_queue = Application.get_env(:farside, :max_fail_rate, 50) + 5
status =
case Enum.count(status) < max_queue do
true -> status
false -> []
end
state = %{state | status: status}
state = %{state | last_update: DateTime.utc_now()}
healthy = "#{state.type}_healthy"
unhealthy = "#{state.type}_unhealthy"
dead = "#{state.type}_dead"
Registry.unregister_match(:status, "healthy", state.url)
Registry.unregister_match(:status, "unhealthy", state.url)
Registry.unregister_match(:status, "dead", state.url)
Registry.unregister_match(:status, healthy, state.url)
Registry.unregister_match(:status, unhealthy, state.url)
Registry.unregister_match(:status, dead, state.url)
dt =
DateTime.utc_now()
|> DateTime.add(-60, :second)
state =
if reply != :good do
filtered = Enum.reject(status, fn x -> x == :good end)
case DateTime.compare(dt, state.last_update) do
:gt ->
reply = Http.test_service(state)
fails_before_death = Application.get_env(:farside, :max_fail_rate, 50)
status = state.status ++ [reply]
case Enum.count(filtered) < fails_before_death do
true ->
Registry.register(:status, "unhealthy", state.url)
Registry.register(:status, unhealthy, state.url)
max_queue = Application.get_env(:farside, :max_fail_rate, 50) + 5
status =
case Enum.count(status) < max_queue do
true -> status
false -> []
end
state = %{state | status: status}
state = %{state | last_update: DateTime.utc_now()}
healthy = "#{state.type}_healthy"
unhealthy = "#{state.type}_unhealthy"
dead = "#{state.type}_dead"
Registry.unregister_match(:status, "healthy", state.url)
Registry.unregister_match(:status, "unhealthy", state.url)
Registry.unregister_match(:status, "dead", state.url)
Registry.unregister_match(:status, healthy, state.url)
Registry.unregister_match(:status, unhealthy, state.url)
Registry.unregister_match(:status, dead, state.url)
if reply != :good do
filtered = Enum.reject(status, fn x -> x == :good end)
fails_before_death = Application.get_env(:farside, :max_fail_rate, 50)
case Enum.count(filtered) < fails_before_death do
true ->
Registry.register(:status, "unhealthy", state.url)
Registry.register(:status, unhealthy, state.url)
state
false ->
Registry.register(:status, "dead", state.url)
Registry.register(:status, dead, state.url)
%{state | status: [:bad]}
end
else
Registry.register(:status, "healthy", state.url)
Registry.register(:status, healthy, state.url)
state
end
false ->
Registry.register(:status, "dead", state.url)
Registry.register(:status, dead, state.url)
%{state | status: [:bad]}
end
else
Registry.register(:status, "healthy", state.url)
Registry.register(:status, healthy, state.url)
state
_ ->
%{state | last_update: DateTime.utc_now()}
end
{:noreply, state}
end
@impl true
def handle_cast(
:shutdown,
state
) do
{:stop, :normal, state}
end
@doc false
def via_tuple(id, registry \\ @registry_name) do
{:via, Registry, {registry, id}}
end
@impl true
def handle_info({:DOWN, _ref, :process, _pid, _reason}, data) do
{:noreply, data}
end
@impl true
def handle_info(_msg, state) do
{:noreply, state}
end
end

View File

@ -1,6 +1,11 @@
defmodule Farside.Service.Supervisor do
use DynamicSupervisor
@moduledoc """
Service Supervisor
this will supervise the service
"""
alias __MODULE__, as: SUPERVISOR
alias Farside.Service, as: SERVER

View File

@ -2,6 +2,8 @@ defmodule Farside.Throttle do
import Plug.Conn
use PlugAttack
@moduledoc nil
rule "throttle per ip", conn do
# throttle to 1 request per second
throttle(conn.remote_ip,

View File

@ -5,6 +5,8 @@ defmodule Farside.Server.UnHealthyCheck do
use Task
alias Farside.LastUpdated
require Logger
def child_spec(args) do
%{
id: __MODULE__,
@ -20,7 +22,7 @@ defmodule Farside.Server.UnHealthyCheck do
def poll() do
receive do
after
120_000 ->
200_000 ->
run()
poll()
end
@ -29,6 +31,8 @@ defmodule Farside.Server.UnHealthyCheck do
def run() do
LastUpdated.value(DateTime.utc_now())
Logger.info("Unhealthy Service Check Running")
Registry.dispatch(:status, "unhealthy", fn entries ->
for {pid, _} <- entries, do: GenServer.cast(pid, :check)
end)

View File

@ -1,4 +1,6 @@
defmodule Service do
@moduledoc nil
defstruct type: nil,
test_url: nil,
fallback: nil,

View File

@ -41,6 +41,8 @@ defmodule Farside.MixProject do
{:jason, "~> 1.1"},
{:plug_attack, "~> 0.4.2"},
{:plug_cowboy, "~> 2.0"},
{:credo, "~> 1.6.3", only: [:dev, :test], runtime: false},
{:mix_audit, "~> 1.0.0", only: [:dev, :test], runtime: false},
{:bakeware, runtime: false, only: :cli}
]
end

View File

@ -1,13 +1,16 @@
%{
"artificery": {:hex, :artificery, "0.4.3", "0bc4260f988dcb9dda4b23f9fc3c6c8b99a6220a331534fdf5bf2fd0d4333b02", [:mix], [], "hexpm", "12e95333a30e20884e937abdbefa3e7f5e05609c2ba8cf37b33f000b9ffc0504"},
"bakeware": {:hex, :bakeware, "0.2.4", "0aaf49b34f4bab2aa433f9ff1485d9401e421603160abd6d269c469fc7b65212", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "7b97bcf6fbeee53bb32441d6c495bf478d26f9575633cfef6831e421e86ada6d"},
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"certifi": {:hex, :certifi, "2.8.0", "d4fb0a6bb20b7c9c3643e22507e42f356ac090a1dcea9ab99e27e0376d695eba", [:rebar3], [], "hexpm", "6ac7efc1c6f8600b08d625292d4bbf584e14847ce1b6b5c44d983d273e1097ea"},
"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"},
"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"},
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
"credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"},
"crontab": {:hex, :crontab, "1.1.10", "dc9bb1f4299138d47bce38341f5dcbee0aa6c205e864fba7bc847f3b5cb48241", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "1347d889d1a0eda997990876b4894359e34bfbbd688acbb0ba28a2795ca40685"},
"distillery": {:hex, :distillery, "2.1.1", "f9332afc2eec8a1a2b86f22429e068ef35f84a93ea1718265e740d90dd367814", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm", "bbc7008b0161a6f130d8d903b5b3232351fccc9c31a991f8fcbf2a12ace22995"},
"elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"gen_stage": {:hex, :gen_stage, "1.1.2", "b1656cd4ba431ed02c5656fe10cb5423820847113a07218da68eae5d6a260c23", [:mix], [], "hexpm", "9e39af23140f704e2b07a3e29d8f05fd21c2aaf4088ff43cb82be4b9e3148d02"},
"hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"},
"httpoison": {:hex, :httpoison, "1.8.0", "6b85dea15820b7804ef607ff78406ab449dd78bed923a49c7160e1886e987a3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "28089eaa98cf90c66265b6b5ad87c59a3729bea2e74e9d08f9b51eb9729b3c3a"},
@ -16,6 +19,7 @@
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "2.0.2", "0b9e1a4c840eafb68d820b0e2158ef5c49385d17fb36855ac6e7e087d4b1dcc5", [:mix], [], "hexpm", "e6a3f76b4c277739e36c2e21a2c640778ba4c3846189d5ab19f97f126df5f9b7"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mix_audit": {:hex, :mix_audit, "1.0.1", "9dd114408961b8db214f42fee40b2f632ecd7e4fd29500403068c82c77db8361", [:make, :mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.8.0", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "65066bb7757078aa49faaa2f7c1e2d52f56ff6fe6cff01723dbaf5be2a75771b"},
"mustache": {:hex, :mustache, "0.3.1", "4c6ee79b13aae954035fe31b83c94480ddc7b536d09c44d4c65e61a9ead38d6b", [:mix], [], "hexpm", "8dc92b9b92a0d7449628f4fc981f8018a16a5b8c9907249e59db461482dac143"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"phoenix_view": {:hex, :phoenix_view, "1.0.0", "fea71ecaaed71178b26dd65c401607de5ec22e2e9ef141389c721b3f3d4d8011", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "82be3e2516f5633220246e2e58181282c71640dab7afc04f70ad94253025db0c"},
@ -30,4 +34,6 @@
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
"yaml_elixir": {:hex, :yaml_elixir, "2.8.0", "c7ff0034daf57279c2ce902788ce6fdb2445532eb4317e8df4b044209fae6832", [:mix], [{:yamerl, "~> 0.8", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "4b674bd881e373d1ac6a790c64b2ecb69d1fd612c2af3b22de1619c15473830b"},
}