Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --endpoint option for beacon.gen.site #761

Merged
merged 6 commits into from
Feb 20, 2025
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
71 changes: 37 additions & 34 deletions lib/mix/tasks/beacon.gen.site.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ defmodule Mix.Tasks.Beacon.Gen.Site.Docs do
* `--host` (optional) - If provided, site will be served on that host.
* `--port` (optional) - The port to use for http requests. Only needed when `--host` is provided. If no port is given, one will be chosen at random.
* `--secure-port` (optional) - The port to use for https requests. Only needed when `--host` is provided. If no port is given, one will be chosen at random.
* `--endpoint` (optional) - The name of the Endpoint Module for your site. If not provided, a default will be generated based on the `site`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on the name, Igniter will try to create files in a path that users may not expect. For eg:

--endpoint DockYardWeb.Endpoint

Creates the file lib/dock_yard_web/endpoint.ex

Which is expected because it follows the Phoenix convention. Are we just follow that pattern or find a way to overwrite the path name as well?

For example, `beacon.gen.site --site my_site` will use `MySiteEndpoint`
* `--secret-key-base` (optional) - The value to use for secret_key_base in your app config.
By default, Beacon will generate a new value and update all existing config to match that value.
If you don't want this behavior, copy the secret_key_base from your app config and provide it here.
Expand Down Expand Up @@ -70,6 +72,7 @@ if Code.ensure_loaded?(Igniter) do
host: :string,
port: :integer,
secure_port: :integer,
endpoint: :string,
secret_key_base: :string,
signing_salt: :string,
session_key: :string,
Expand Down Expand Up @@ -102,6 +105,12 @@ if Code.ensure_loaded?(Igniter) do
port = Keyword.get_lazy(options, :port, fn -> Enum.random(4101..4999) end)
secure_port = Keyword.get_lazy(options, :secure_port, fn -> Enum.random(8444..8999) end)

site_endpoint =
options
|> Keyword.get_lazy(:endpoint, fn -> site_endpoint_module!(igniter, site) end)
|> List.wrap()
|> Module.concat()

otp_app = Igniter.Project.Application.app_name(igniter)
web_module = Igniter.Libs.Phoenix.web_module(igniter)
{igniter, router} = Beacon.Igniter.select_router!(igniter)
Expand All @@ -112,13 +121,13 @@ if Code.ensure_loaded?(Igniter) do
|> add_use_beacon_in_router(router)
|> add_beacon_pipeline_in_router(router)
|> mount_site_in_router(router, site, path, host)
|> add_site_config_in_config_runtime(site, repo, router)
|> add_site_config_in_config_runtime(site, repo, router, site_endpoint)
|> add_beacon_config_in_app_supervisor(site, repo)
|> create_proxy_endpoint(argv)
|> create_new_endpoint(site, otp_app, web_module)
|> configure_new_endpoint(site, host, otp_app, port, secure_port)
|> create_site_endpoint(otp_app, web_module, site_endpoint)
|> configure_site_endpoint(host, otp_app, port, secure_port, site_endpoint)
|> update_session_options(otp_app)
|> add_new_endpoint_to_application(site, repo)
|> add_site_endpoint_to_application(repo, site_endpoint)
|> Igniter.add_notice("""
Site #{inspect(site)} generated successfully.

Expand Down Expand Up @@ -284,13 +293,11 @@ if Code.ensure_loaded?(Igniter) do
{:scope, scope, [scope_path, updated_opts, rest]}
end

defp add_site_config_in_config_runtime(igniter, site, repo, router) do
endpoint = new_endpoint_module!(igniter, site)

defp add_site_config_in_config_runtime(igniter, site, repo, router, site_endpoint) do
igniter
|> Igniter.Project.Config.configure("runtime.exs", :beacon, [site, :site], {:code, Sourceror.parse_string!(":#{site}")})
|> Igniter.Project.Config.configure("runtime.exs", :beacon, [site, :repo], {:code, Sourceror.parse_string!(inspect(repo))})
|> Igniter.Project.Config.configure("runtime.exs", :beacon, [site, :endpoint], {:code, Sourceror.parse_string!(inspect(endpoint))})
|> Igniter.Project.Config.configure("runtime.exs", :beacon, [site, :endpoint], {:code, Sourceror.parse_string!(inspect(site_endpoint))})
|> Igniter.Project.Config.configure("runtime.exs", :beacon, [site, :router], {:code, Sourceror.parse_string!(inspect(router))})
end

Expand Down Expand Up @@ -336,7 +343,7 @@ if Code.ensure_loaded?(Igniter) do

defp create_proxy_endpoint(igniter, argv), do: Igniter.compose_task(igniter, "beacon.gen.proxy_endpoint", argv)

defp create_new_endpoint(igniter, site, otp_app, web_module) do
defp create_site_endpoint(igniter, otp_app, web_module, site_endpoint) do
proxy_endpoint_module_name = Igniter.Libs.Phoenix.web_module_name(igniter, "ProxyEndpoint")

contents = """
Expand Down Expand Up @@ -384,29 +391,28 @@ if Code.ensure_loaded?(Igniter) do

Igniter.Project.Module.find_and_update_or_create_module(
igniter,
new_endpoint_module!(igniter, site),
site_endpoint,
contents,
fn zipper -> {:ok, Igniter.Code.Common.replace_code(zipper, contents)} end
)
end

defp configure_new_endpoint(igniter, site, host, otp_app, port, secure_port) do
new_endpoint = new_endpoint_module!(igniter, site)
defp configure_site_endpoint(igniter, host, otp_app, port, secure_port, site_endpoint) do
error_html = "Beacon.Web.ErrorHTML"
pubsub = Igniter.Project.Module.module_name(igniter, "PubSub")

# TODO: replace the first two steps with `configure/6` once the `:after` option is allowed
igniter
|> then(
&if(Igniter.Project.Config.configures_key?(&1, "config.exs", otp_app, new_endpoint),
&if(Igniter.Project.Config.configures_key?(&1, "config.exs", otp_app, site_endpoint),
do: &1,
else:
Igniter.update_elixir_file(&1, Beacon.Igniter.config_file_path(igniter, "config.exs"), fn zipper ->
{:ok,
zipper
|> Beacon.Igniter.move_to_variable!(:signing_salt)
|> Igniter.Project.Config.modify_configuration_code(
[new_endpoint],
[site_endpoint],
otp_app,
Sourceror.parse_string!("""
[
Expand All @@ -425,15 +431,15 @@ if Code.ensure_loaded?(Igniter) do
)
)
|> then(
&if(Igniter.Project.Config.configures_key?(&1, "dev.exs", otp_app, new_endpoint),
&if(Igniter.Project.Config.configures_key?(&1, "dev.exs", otp_app, site_endpoint),
do: &1,
else:
Igniter.update_elixir_file(&1, Beacon.Igniter.config_file_path(igniter, "dev.exs"), fn zipper ->
{:ok,
zipper
|> Beacon.Igniter.move_to_variable!(:secret_key_base)
|> Igniter.Project.Config.modify_configuration_code(
[new_endpoint],
[site_endpoint],
otp_app,
Sourceror.parse_string!("""
[
Expand All @@ -448,33 +454,33 @@ if Code.ensure_loaded?(Igniter) do
end)
)
)
|> Igniter.Project.Config.configure_runtime_env(:prod, otp_app, [new_endpoint, :url, :host], host || {:code, Sourceror.parse_string!("host")},
|> Igniter.Project.Config.configure_runtime_env(:prod, otp_app, [site_endpoint, :url, :host], host || {:code, Sourceror.parse_string!("host")},
updater: &{:ok, Sourceror.Zipper.replace(&1, host || Sourceror.parse_string!("host"))}
)
|> Igniter.Project.Config.configure_runtime_env(:prod, otp_app, [new_endpoint, :url, :port], secure_port,
|> Igniter.Project.Config.configure_runtime_env(:prod, otp_app, [site_endpoint, :url, :port], secure_port,
updater: &{:ok, Sourceror.Zipper.replace(&1, Sourceror.parse_string!("#{secure_port}"))}
)
|> Igniter.Project.Config.configure_runtime_env(:prod, otp_app, [new_endpoint, :url, :scheme], "https",
|> Igniter.Project.Config.configure_runtime_env(:prod, otp_app, [site_endpoint, :url, :scheme], "https",
updater: &{:ok, Sourceror.Zipper.replace(&1, "https")}
)
|> Igniter.Project.Config.configure_runtime_env(
:prod,
otp_app,
[new_endpoint, :http],
[site_endpoint, :http],
{:code, Sourceror.parse_string!("[ip: {0, 0, 0, 0, 0, 0, 0, 0}, port: #{port}]")},
updater: &{:ok, Sourceror.Zipper.replace(&1, Sourceror.parse_string!("[ip: {0, 0, 0, 0, 0, 0, 0, 0}, port: #{port}]"))}
)
|> Igniter.Project.Config.configure_runtime_env(
:prod,
otp_app,
[new_endpoint, :secret_key_base],
[site_endpoint, :secret_key_base],
{:code, Sourceror.parse_string!("secret_key_base")},
updater: &{:ok, Sourceror.Zipper.replace(&1, Sourceror.parse_string!("secret_key_base"))}
)
|> Igniter.Project.Config.configure_runtime_env(
:prod,
otp_app,
[new_endpoint, :server],
[site_endpoint, :server],
{:code, Sourceror.parse_string!("!!System.get_env(\"PHX_SERVER\")")},
updater: &{:ok, Sourceror.Zipper.replace(&1, Sourceror.parse_string!("!!System.get_env(\"PHX_SERVER\")"))}
)
Expand All @@ -490,22 +496,19 @@ if Code.ensure_loaded?(Igniter) do
)
end

defp add_new_endpoint_to_application(igniter, site, repo) do
Igniter.Project.Application.add_new_child(igniter, new_endpoint_module!(igniter, site), after: [repo, Phoenix.PubSub, Beacon])
defp add_site_endpoint_to_application(igniter, repo, site_endpoint) do
Igniter.Project.Application.add_new_child(igniter, site_endpoint, after: [repo, Phoenix.PubSub, Beacon])
end

@doc false
def new_endpoint_module!(site) when is_atom(site) do
site
|> to_string()
|> String.split(~r/[^[:alnum:]]+/)
|> Enum.map_join("", &String.capitalize/1)
|> Kernel.<>("Endpoint")
end
def site_endpoint_module!(igniter, site) when is_atom(site) do
suffix =
site
|> to_string()
|> String.split(~r/[^[:alnum:]]+/)
|> Enum.map_join("", &String.capitalize/1)
|> Kernel.<>("Endpoint")

@doc false
def new_endpoint_module!(igniter, site) when is_atom(site) do
suffix = new_endpoint_module!(site)
Igniter.Libs.Phoenix.web_module_name(igniter, suffix)
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/mix/tasks/beacon.gen.tailwind_config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ if Code.ensure_loaded?(Igniter) do
site = Keyword.fetch!(options, :site) |> String.to_atom()

app_name = Igniter.Project.Application.app_name(igniter)
endpoint = Mix.Tasks.Beacon.Gen.Site.new_endpoint_module!(igniter, site)
endpoint = Mix.Tasks.Beacon.Gen.Site.site_endpoint_module!(igniter, site)

igniter
|> create_tailwind_config()
Expand Down
49 changes: 49 additions & 0 deletions test/mix/tasks/gen_site_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -356,5 +356,54 @@ defmodule Mix.Tasks.Beacon.GenSiteTest do
end
""")
end

test "accepts custom site endpoint module name" do
phoenix_project()
|> Igniter.compose_task("beacon.gen.site", @opts_my_site ++ ~w(--endpoint TestWeb.CustomEndpoint))
|> assert_creates("lib/test_web/custom_endpoint.ex", """
defmodule TestWeb.CustomEndpoint do
use Phoenix.Endpoint, otp_app: :test

@session_options Application.compile_env!(:test, :session_options)

def proxy_endpoint, do: TestWeb.ProxyEndpoint

# socket /live must be in the proxy endpoint

# Serve at "/" the static files from "priv/static" directory.
#
# You should set gzip to true if you are running phx.digest
# when deploying your static files in production.
plug Plug.Static,
at: "/",
from: :test,
gzip: false,
# robots.txt is served by Beacon
only: ~w(assets fonts images favicon.ico)

# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
plug Phoenix.CodeReloader
plug Phoenix.Ecto.CheckRepoStatus, otp_app: :test
end

plug Plug.RequestId
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]

plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Phoenix.json_library()

plug Plug.MethodOverride
plug Plug.Head
plug Plug.Session, @session_options
plug TestWeb.Router
end
""")
end
end
end
Loading