livebook-dev / livebook

Automate code & data workflows with interactive Elixir notebooks
https://livebook.dev
Apache License 2.0
4.98k stars 427 forks source link

Help: Running umbrella phoenix application as dependency #2752

Closed BrooklinJazz closed 2 months ago

BrooklinJazz commented 2 months ago

I'm running an umbrella phoenix application as a dependency to my livebook. (The reason I'm running it this way is because we have a smart cell that lets us dynamically inject a LiveView into the running application for tutorial purposes)

Mix.install(
  [
    {:kino_live_view_native, path: "../")}
  ],
  config: [
    ...
  ],
  force: true
)

However, there's a problem with the code_reloader.

** (Mix.Error) Cannot access build without an application name, please ensure you are in a directory with a mix.exs file and it defines an :app name under the project configuration
    (mix 1.16.2) lib/mix.ex:580: Mix.raise/2
    (mix 1.16.2) lib/mix/project.ex:810: Mix.Project.consolidation_path/1
    (phoenix 1.7.14) lib/phoenix/code_reloader/server.ex:180: Phoenix.CodeReloader.Server.mix_compile/5
    (phoenix 1.7.14) lib/phoenix/code_reloader/server.ex:74: anonymous fn/4 in Phoenix.CodeReloader.Server.handle_call/3
    (phoenix 1.7.14) lib/phoenix/code_reloader/server.ex:299: Phoenix.CodeReloader.Server.proxy_io/1
    (phoenix 1.7.14) lib/phoenix/code_reloader/server.ex:72: Phoenix.CodeReloader.Server.handle_call/3
    (stdlib 5.2) gen_server.erl:1131: :gen_server.try_handle_call/4
    (stdlib 5.2) gen_server.erl:1160: :gen_server.handle_msg/6

This is because the Mix.Project.config/1 function uses the default config, rather than the config from our application's project/1 function. This leads to a error raised by the Mix.Project.consolidation_path/1 function.

We thought we fixed the problem by adding a custom code reloader.

  def reload!(_, _) do
    # This transaction ensures that this function is only called once during the code reload.
    # This change was due to an error where the web app.js would not load properly and web event handlers were broken
    :global.trans({:resource_id, :lock_requester_id}, fn ->
      Mix.in_install_project(fn ->
        _ = Application.ensure_all_started(:hex)
        Mix.Task.rerun("deps.compile", ["kino_live_view_native"])
        Mix.Task.clear()
      end)
    end)
  end

However, it seems like this only hides the issue, by making it so the reload!/2 function doesn't crash. However code reloading does still work, it's just a hacky fix.

How can I add the umbrella project as a dependency such that it runs with the correct config and avoids the Cannot access build without an application name, please ensure you are in a directory with a mix.exs file and it defines an :app name under the project configuration error?

josevalim commented 2 months ago

My suggestion would be to move the notebook to a particular app inside the umbrella (or point to a particular application). Given that application will depend on the other parts of the umbrella that you care about, it should all still work.

BrooklinJazz commented 2 months ago

Unfortunately the umbrella app is something that will be a dependency to many different notebooks.

However, I think making the application a non-umbrella app could be a solution if that's the core problem. I also didn't realize umbrella apps can't be a dependency on hex, so that could be a problem for us in the future (and another reason to use a non-umbrella application)!

Thank you. I'll close the issue.

josevalim commented 2 months ago

To be clear, you don't need to move away from the umbrella. You only need to point directly to one of the apps inside the apps/* folder. In umbrella apps, the umbrella is a convenience, each app is still meant to be usable standalone, and you usually have an app that depends on the majority of other apps anyway. :)

BrooklinJazz commented 2 months ago

Hey @josevalim!

I tested changing the application to a non-umbrella, but the issue persists.

Cannot access build without an application name, please ensure you are in a directory with a mix.exs file and it defines an :app name under the project configuration
    (mix 1.16.2) lib/mix.ex:580: Mix.raise/2
    (mix 1.16.2) lib/mix/project.ex:810: Mix.Project.consolidation_path/1
    (phoenix 1.7.14) lib/phoenix/code_reloader/server.ex:180: Phoenix.CodeReloader.Server.mix_compile/5
    (phoenix 1.7.14) lib/phoenix/code_reloader/server.ex:74: anonymous fn/4 in Phoenix.CodeReloader.Server.handle_call/3
    (phoenix 1.7.14) lib/phoenix/code_reloader/server.ex:299: Phoenix.CodeReloader.Server.proxy_io/1
    (phoenix 1.7.14) lib/phoenix/code_reloader/server.ex:72: Phoenix.CodeReloader.Server.handle_call/3
    (stdlib 5.2) gen_server.erl:1131: :gen_server.try_handle_call/4
    (stdlib 5.2) gen_server.erl:1160: :gen_server.handle_msg/6

It seems like perhaps running any Phoenix application as a dependency from Livebook doesn't work 🤔 I can "hide" the bug by using a custom code reloader so I'm not blocked. However, I want to re-open the issue incase this is a larger bug for Livebook.

I think the Mix.Project.app_path function is broken when running from Livebook. (That's the function Mix.Project.consolidation_path/1 calls)

Screenshot 2024-08-18 at 7 03 19 PM

That's ultimately what causes my problem running a Phoenix project as a dependency inside of Livebook.

BrooklinJazz commented 2 months ago

Feel free to close this if you don't think it's an issue for Livebook :)

jonatanklosko commented 2 months ago

@BrooklinJazz referncing Mix.Project in Livebook doesn't work, because you are not actively in the Mix.install(...) project, same as in scripts or IEx. If you want to work against the mix install project, technically you can do this:

Mix.in_install_project(fn ->
  Mix.Project.app_path()
end)

This is a private API at the moment though.

josevalim commented 2 months ago

Thank you @BrooklinJazz for the follow up. Good to know this was not umbrella specific (btw: I have edited my comment above that implied this to be the case to avoid future confusion).

So it seems the root cause, as Jonatan said, is that there is indeed no Mix project running, so the code reloader won't work, with or without umbrellas. Could you disable the code loading instead? For example, it is disabled in test/prod by default, and maybe it should be disabled when running from Livebook? In fact, dependencies run in the production environment by default, so I would expect it to be disabled. Unless you need it enabled for other reasons?

BrooklinJazz commented 2 months ago

Disabling code reloading isn't an option for us. We inject LiveViews from within the Livebook (for interactive tutorials) and rely on code reloading to update the server whenever someone edits and re-evaluates one of the smart cells.

Disabling code reloading would mean we couldn't edit LiveViews inside of the smart cells

josevalim commented 2 months ago

Got it. So overall, I think a custom code reloader is the way to go, especially because Phoenix only attempts to compile the current project and not the dependency. Ship it!