Current section

Files

Jump to
sentry lib mix tasks sentry.install.ex
Raw

lib/mix/tasks/sentry.install.ex

defmodule Mix.Tasks.Sentry.Install.Docs do
@moduledoc false
def short_doc do
"Installs sentry. Requires igniter to be installed."
end
def example do
"mix sentry.install --dsn <your_dsn>"
end
def long_doc do
"""
#{short_doc()}
This installer is built with, and requires, igniter to be used. Igniter is a tool
for biulding package installers. For information on how to add igniter to your
project, see the documentation https://hexdocs.pm/igniter/readme.html#installation.
## Example
```bash
#{example()}
```
## Options
* `--dsn` - Your Sentry DSN.
"""
end
end
if Code.ensure_loaded?(Igniter) do
defmodule Mix.Tasks.Sentry.Install do
@shortdoc "#{__MODULE__.Docs.short_doc()}"
@moduledoc __MODULE__.Docs.long_doc()
use Igniter.Mix.Task
@impl Igniter.Mix.Task
def info(_argv, _composing_task) do
%Igniter.Mix.Task.Info{
group: :sentry,
adds_deps: [{:jason, "~> 1.4"}, {:finch, "~> 0.21"}],
example: __MODULE__.Docs.example(),
schema: [dsn: :string],
defaults: [dsn: "<your_dsn>"]
}
end
@impl Igniter.Mix.Task
def igniter(igniter) do
igniter
|> Igniter.Project.Config.configure(
"prod.exs",
:sentry,
[:dsn],
igniter.args.options[:dsn]
)
|> Igniter.Project.Config.configure(
"prod.exs",
:sentry,
[:environment_name],
{:code, quote(do: config_env())}
)
|> Igniter.Project.Config.configure(
"prod.exs",
:sentry,
[:enable_source_code_context],
true
)
|> Igniter.Project.Config.configure(
"prod.exs",
:sentry,
[:root_source_code_paths],
{:code, quote(do: [File.cwd!()])}
)
|> configure_phoenix()
|> add_logger_handler()
|> Igniter.add_notice("""
Sentry:
Add a call to
mix sentry.package_source_code
in your release script to make sure the stacktraces you receive
are correctly categorized.
""")
end
defp add_logger_handler(igniter) do
app_module = Igniter.Project.Application.app_module(igniter)
Igniter.Project.Module.find_and_update_module(igniter, app_module, fn zipper ->
with {:ok, zipper} <- Igniter.Code.Function.move_to_def(zipper, :start, 2),
:error <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
{:logger, :add_handler},
[2, 3, 4],
&Igniter.Code.Function.argument_equals?(&1, 1, Sentry.LoggerHandler)
) do
code =
"""
:logger.add_handler(:sentry_handler, Sentry.LoggerHandler, %{
config: %{metadata: [:file, :line]}
})
"""
{:ok, Igniter.Code.Common.add_code(zipper, code, placement: :before)}
else
_ -> {:ok, zipper}
end
end)
|> case do
{:ok, igniter} -> igniter
_ -> igniter
end
end
defp configure_phoenix(igniter) do
{igniter, routers} =
Igniter.Libs.Phoenix.list_routers(igniter)
{igniter, endpoints} =
Enum.reduce(routers, {igniter, []}, fn router, {igniter, endpoints} ->
{igniter, new_endpoints} = Igniter.Libs.Phoenix.endpoints_for_router(igniter, router)
{igniter, endpoints ++ new_endpoints}
end)
Enum.reduce(endpoints, igniter, fn endpoint, igniter ->
igniter
|> setup_endpoint(endpoint)
end)
end
defp setup_endpoint(igniter, endpoint) do
# Sentry.PlugCapture is only recommended for Cowboy; on Bandit it can
# result in duplicate errors.
uses_cowboy? = Igniter.Project.Deps.has_dep?(igniter, :plug_cowboy)
Igniter.Project.Module.find_and_update_module!(igniter, endpoint, fn zipper ->
zipper
|> maybe_add_plug_capture(uses_cowboy?)
|> Igniter.Code.Common.within(&add_plug_context/1)
|> then(&{:ok, &1})
end)
end
defp maybe_add_plug_capture(zipper, true) do
Igniter.Code.Common.within(zipper, &add_plug_capture/1)
end
defp maybe_add_plug_capture(zipper, false), do: zipper
defp add_plug_capture(zipper) do
with :error <- Igniter.Code.Module.move_to_use(zipper, Sentry.PlugCapture),
{:ok, zipper} <- Igniter.Code.Module.move_to_use(zipper, Phoenix.Endpoint) do
Igniter.Code.Common.add_code(zipper, "use Sentry.PlugCapture", placement: :before)
else
_ ->
zipper
end
end
defp add_plug_context(zipper) do
with :error <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:plug,
[1, 2],
&Igniter.Code.Function.argument_equals?(&1, 0, Sentry.PlugContext)
),
{:ok, zipper} <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:plug,
[1, 2],
&Igniter.Code.Function.argument_equals?(&1, 0, Plug.Parsers)
) do
Igniter.Code.Common.add_code(zipper, "plug Sentry.PlugContext", placement: :after)
else
_ ->
zipper
end
end
end
else
defmodule Mix.Tasks.Sentry.Install do
@shortdoc "#{__MODULE__.Docs.short_doc()} | Install `igniter` to use"
@moduledoc __MODULE__.Docs.long_doc()
use Mix.Task
def run(_argv) do
Mix.shell().error("""
The task 'sentry.install' requires igniter. Please install igniter and try again.
For more information, see: https://hexdocs.pm/igniter/readme.html#installation
""")
exit({:shutdown, 1})
end
end
end