Qqwy

Qqwy

TypeCheck Core Team

I am working on a project that is almost, but not quite, an umbrella application. Rather, it contains many mix-projects in subfolders of the apps/ folder, with multiple Mix projects depending on varying groups of these in subfolders of the rels/ folder.

A minimal example setup can be seen in this GitHub repo


The commands to create it were:

$ mkdir apps/
$ mkdir rels/

$ cd apps/
$ mix phx.new phoenix_app --no-ecto
$ cd ../rels
$ mix new main_app

Now edit rels/main_app/mix.exs to include the following in the deps() section:

  defp deps do
      {:phoenix_app, path: "../../apps/phoenix_app", env: Mix.env()}
  end

And edit rels/main_app/config/config.exs to include the Phoenix-related configuration of the dependency:

import_config "../../../apps/phoenix_app/config/config.exs"

This is the bare minimum to get Phoenix running in development (as well as in production) with this not-quite-umbrella setup, using iex -S mix phx.server from the main_app folder.

However, there are two gotcha’s:

  • Live code reloading is not working. Making a change to one of the files in apps/phoenix_app/lib/phoenix_app_web/templates/ for instance does not auto-refresh the page. Even manually refreshing the page will not pick up this change.
  • Webpack works, but only if someone went to apps/phoenix_app/assets and ran the npm install && node node_modules/webpack/bin/webpack.js --mode development there. That will work fine for the person who first set up the applications (because Phoenix asks you if you want to ‘fetch and install dependencies now’). But when another contributor fetches the application from Git later, they won’t have the same luck. They need to manually run those commands. They do get a warning when starting up the app to run cd assets && npm install, but this will not point to the path the Phoenix app itself is installed in, and thus be confusing.

I am trying to resolve these two issues.
What configuration am I missing to make this work without issue?
Feel free to look at and try things out with the minimal example application yourself :slight_smile:.

Thanks!~

Showing Posts 1 to 10

josevalim

josevalim

Creator of Elixir

You would need to change Phoenix’ code reloader to look at path deps instead of umbrella children.

Qqwy

Qqwy OP

TypeCheck Core Team

Thank you for your reply!

It is not currently running in an umbrella, i.e. the Phoenix app was not created using the --umbrella setting, and there is no in_umbrella: true in the deps().

In the configuration (apps/phoenix_app/config/dev.exs) the code reloader responds to the following regexps:

config :phoenix_app, PhoenixAppWeb.Endpoint,
  live_reload: [
    patterns: [
      ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
      ~r"priv/gettext/.*(po)$",
      ~r"lib/phoenix_app_web/{live,views}/.*(ex)$",
      ~r"lib/phoenix_app_web/templates/.*(eex)$"
    ]
  ]

These seem to match any paths with the given suffixes, regardless of what appears in front (since the regular expression patterns do not start with a ^). Alas, that does not seem to work.

So what setting needs to be changed here exactly?

axelson

axelson

Scenic Core Team

You might want to look at the reloadable_apps configuration of the CodeReloader: Phoenix.CodeReloader — Phoenix v1.8.8

(also make sure that you’re making a distinction in your head between the CodeReloader, which compiles and loads new versions of the beam files, and the phoenix_live_reload which triggers page refreshes when files change).

You might also want to take a look at exsync (which I am a maintainer of). You could probably use the reload_callback to trigger a page refresh:

Exsync will reload all path dependencies by default, unlike Phoenix.CodeReloader

josevalim

josevalim

Creator of Elixir

That’s my point. Phoenix Code Reloader can do what you are asking for umbrellas, but not for path dependencies. Someone would have to generalize the existing code.

sorentwo

sorentwo

Oban Core Team

Thanks for bringing this up, and for the link to exsync. This has been an annoyance while developing the Oban ui for a long time now—it is an isolated set of live views and needs to be “hosted” in a proper Phoenix app. I resorted to symlinking to get reloading to work for modules, though “live reload” hasn’t worked at all.

I would imagine that the Phoenix live dashboard has similar development constraints.

josevalim

josevalim

Creator of Elixir

@sorentwo we start a single file Phoenix application for the dashboard and we configured it with code reloading, so things work fine there: phoenix_live_dashboard/dev.exs at main · phoenixframework/phoenix_live_dashboard · GitHub

Qqwy

Qqwy OP

TypeCheck Core Team

Thank you, it was not immediately clear to me that you expected changes to the code itself to be required.

After looking at the implementation, especially here(where the Endpoint’s :reloadable_apps configuration is read) and here (where the default fallback is set, that has a special case for an umbrella application) I figured out that manually setting this configuration to [:phoenix_app] (e.g. the name of your OTP application that runs phoenix) will make the code reloader work:

config :phoenix_app, PhoenixAppWeb.Endpoint,
  # ... other settings here,
  # ... with at the end:
  reloadable_apps: [:phoenix_app]

(See also this commit on the example repository.)

This does not do the live code reloading yet (and I’ll investigate further to figure that out), but at least it will load the new code after a page refresh, which might be good enough for some applications.

sorentwo

sorentwo

Oban Core Team

Of course you did https://www.youtube.com/watch?v=TslkdT3PfKc

Great idea! I did something very similar for test mode, but I neglected to try it for dev. So much to learn from phoenix_live_dashboard :yellow_heart:

alon

alon

Edit:
I posted what may be a better solution in a follow-up post, so you might want to skip this one and read the follow-up first.


I have been suffering from this annoyance as well.

Today I decided to try and figure out if I could do something better and tinkered with the phoenix_live_reload pakcaage in my _deps to see what is receives.

My setup is similar to the one described in the post, including various applications that depend on one another. This includes core apps, a phoenix front-end, APIs, and meta-apps that compose them into releases.

Often, when developing the Phoenix-based app I would run it directly in order to get live reload working.
However, it was an incomplete compromise, as I wanted some of the APIs that live in other apps, or the entire distribution, to be running as well.

For the sake of example, I have 3 apps:
:my_app_core (MyApp.Core namespace), :my_app_phoenix (MyApp.Phoenix namespace) that is the Phoenix UI, and :my_app_app1 (MyApp.App1 namespace), that is intended for distribution.

app1 depends on phoenix, and phoenix depends on core.

I wanted to get live-reload when running app1.


When running :my_app_phoenix directly, the live_reload socket would see file changes in the form of
lib/my_app_phoenix/components/layout_components/navbar.heex, the configured reload patterns would match and the pages would reload.


When runnign app1:
The dependencies between the apps are library path deps (i.e, {:my_app_phoenix, path: "../my_app_phoenix"} and it’s not an umbrella app.
This means that in the scope of my_app_app1, changes to my_app_phoenix get compiled into app1’s _build directory, and that is what live_reload reported when checking for file changes.

For example, /Users/<snip>/my_app_app1/_build/dev/lib/my_app_phoenix/ebin/Elixir.MyApp.Phoenix.Components.LayoutComponents.beam, for the module that contains the template in the previous example.

Therefore, I changed the patterns in my_app_app1’s dev config to include:

  ~r"my_app_phoenix/ebin/Elixir.MyApp.Phoenix.(Components|Layout|Views).*.beam$",

and now changes to templates that get compiled into those modules, or those modules themselves, are causing the page to reload. The appropriate pattern depends on the app’s module structure, of course.


One thing to note is that those are full-page reloads, and that live_reload may be able to do more subtle refreshes to the UI. The live_reload socket had reported {"asset_type": "heex"} when I ran my_app_phoenix directly and the changed to the template file was directly picked up, and {"asset_type": "beam"} when I ran my_app_app1 and it picked up the change to the compiled module.

I didn’t test this thoroughly yet, and may need to enhance my setup later, but as a first step this is a significant improvement for me.

alon

alon

After a bit more exploration, I noticed that (as I suspected), changes to css were not being picked up.

I dug some more and looked at live_reloader’s application. It has a dirs option, which is used for setting up filesystem watches.

I configured it to watch the my_app_phoenix app directory, and commented out the beam watch for now.

Reloading assets (js, css), as well as file chagnes seems to work as it does when running my_app_phoenix directly.

Here is the current config I’m using:

config :my_app_phoenix, MyApp.Phoenix.Endpoint,
  live_reload: [
    patterns: [
      ~r"my_app_phoenix/priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
      ~r"my_app_phoenix/priv/gettext/.*(po)$",
      ~r"my_app_phoenix/lib/my_app_phoenix/(live|views|components)/.*(ex)$",
      ~r"my_app_phoenix/lib/my_app_phoenix/templates/.*(eex)$",
      # ~r"my_app_phoenix/ebin/Elixir.MyApp.Phoenix.(Components|Layout|Views).*.beam$",
    ]
  ],
  reloadable_apps: [:my_app_phoenix]

config :phoenix_live_reload, :dirs, ["", "../my_app_phoenix"]

The :phoenix_live_reload config causes live_reload to watche both my_app_app1 and my_app_phoenix directories for changes. I may remove the "" value to watch only the phoenix app.
I also suspect that removing the beam watches may cause a race condition where changes to a file would cause a page reload before the file is able to complete its compilation, such that the old version would be rendered on reload, but so far, the browser was displaying the desired results.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New
dillonoconnor
Is there any way to avoid the Hologram compiler running when using iex? It seems like the front-end code could potentially be disregarded...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews