Create an Elixir umbrella project containing a phoenix app and build a release with Distillery

# Create the umbrella project:
mix new chat_umbrella --umbrella
cd chat_umbrella/apps
mix phoenix.new chat_web --no-ecto
...
Fetch and install dependencies? [Yn] y
...
mix new chat_backend --module ChatBackend --sup
mix phoenix.server
# apps/chat_backend/test/chat_backend_test.exs
defmodule ChatBackendTest do
use ExUnit.Case
test "getting the message" do
assert ChatBackend.get_message() == "Hello, from ChatBackend"
end
end
mix.test
==> chat_backend
1) test getting the message (ChatBackendTest)
test/chat_backend_test.exs:4
** (UndefinedFunctionError) function ChatBackend.get_message/0 is undefined or private
Finished in 0.01 seconds
1 test, 1 failure
==> chat_web
....
Finished in 0.04 seconds
4 tests, 0 failures
# apps/chat_backend/lib/chat_backend.ex
defmodule ChatBackend do
def get_message do
"Hello, from ChatBackend"
end
end
# apps/chat_web/lib/web/controllers/page_controller.exs
# Or apps/chat_web/web/controllers/page_controller.exs, depending on your directory structure
defmodule ChatWeb.PageController do
use ChatWeb.Web, :controller
def index(conn, _params) do
message = ChatBackend.get_message()
render conn, "index.html", message: message
end
end
<%= @message %>
def application do
[applications: [:logger]],
mod: {ChatBackend, []}]
end
# apps/chat_backend/lib/chat_backend/message_provider.ex
defmodule ChatBackend.MessageProvider do
use GenServer
@messages ["Message 1", "Message 2", "Message 3"] def start_link(name) do
{:ok, pid} = GenServer.start_link(__MODULE__, :ok, [])
Process.register(pid, name)
{:ok, pid}
end
def init(:ok), do: {:ok, @messages}
def handle_call(:next_message, _from, [h | t]), do: {:reply, h, t ++ [h]}
# Public api:
def get_message(server), do: GenServer.call(server, :next_message)
end
defmodule ChatBackend do
alias ChatBackend.MessageProvider
def start(_type, _args) do
import Supervisor.Spec, warn: false

children = [
worker(MessageProvider, [:message_provider]),
]
opts = [strategy: :one_for_one, name: Test.Supervisor]
Supervisor.start_link(children, opts)
end
def get_message() do
MessageProvider.get_message(:message_provider)
end
end
defp deps do
[{:distillery, "~> 0.9"}]
end
mix release --env=prod
==> Assembling release..
==> Building release chat_umbrella:0.1.0 using environment prod
==> Including ERTS 8.0.2 from /usr/local/Cellar/erlang/19.0.2/lib/erlang/erts-8.0.2
==> Packaging release..
==> Release successfully built!
You can run it in one of the following ways:
Interactive: rel/chat_umbrella/bin/chat_umbrella console
Foreground: rel/chat_umbrella/bin/chat_umbrella foreground
Daemon: rel/chat_umbrella/bin/chat_umbrella start
rel/chat_umbrella/bin/chat_umbrella console
[info] Application chat_web exited: ChatWeb.start(:normal, []) returned an error: shutdown: failed to start child: ChatWeb.Endpoint
** (EXIT) shutdown: failed to start child: Phoenix.CodeReloader.Server
** (UndefinedFunctionError) function Mix.Project.config/0 is undefined (module Mix.Project is not available)
MIX_ENV=prod mix release --env=prod
config :chat_web, ChatWeb.Endpoint,
http: [port: 8080],
url: [host: "localhost", port: 8080],
cache_static_manifest: "priv/static/manifest.json",
server: true
MIX_ENV=prod mix release --env=prod
...
rel/chat_umbrella/bin/chat_umbrella console
...
[info] Running ChatWeb.Endpoint with Cowboy using http://localhost:8080
...
# Run brunch to transpile and compress our assets
cd apps/chat_web
./node_modules/brunch/bin/brunch b -p
- info: compiled 6 files into 2 files, copied 3 in 1.6 sec
MIX_ENV=prod mix phoenix.digest
MIX_ENV=prod mix release --env=prod

--

--

Full-stack developer, specializing in Elixir and React

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Bruce Pomeroy

Bruce Pomeroy

Full-stack developer, specializing in Elixir and React