Fl4m3Ph03n1x

Fl4m3Ph03n1x

How to add extra files to umbrella release?

Background

I have an umbrella project with several apps inside. I want to create a special release that will have additional files inside, like JSON files, configuration, images etc.

I know that in a normal Phoenix project everything inside the priv folder will be in the release under "#{child-app-name}-#{child-app-version}/priv".

Problem

The issue here is that some of my configuration files depend on these assets. So in my config/prod.exs I have tried several ways to get the path dynamically, bu nothing works. The following examples all fail:

config :my_app,
  products: "application-data\\lib\\web_interface-#{Application.spec(:web_interface, :vsn) |> to_string()}\\priv\\persistence\\products.json" 

config :my_app,
  products: "application-data\\lib\\web_interface-#{WebInterface.Mixfile.project()[:version]}\\priv\\persistence\\products.json"

config :my_app,
  products:  "application-data\\lib\\web_interface-#{Application.get_application(:web_interface)}\\priv\\persistence\\products.json"

The one that works is as follows:

products: "application-data\\lib\\web_interface-2.2.0\\priv\\persistence\\products.json",

You can see the issue here is that the version number will change from release to release, and I don’t want to change my prod.exs every time a new release is made.

My solution would be to move the assets I depend on to the root level, so my configuation can access them via "application-data\\assets\\products.json or something similar.

Questions

Is this possible to achieve, given that I am building a tar in my release?

Marked As Solved

LostKobrakai

LostKobrakai

That’s the way to go yes.

Also Liked

LostKobrakai

LostKobrakai

You should be using Application.app_dir(:app_name, "priv/dir/…") in your code to reference files in priv/ folder. You can use configuration for the second parameter if needed.

entone

entone

Additionally you can use the :code.priv_dir/1 function

https://www.erlang.org/doc/man/code.html#priv_dir-1

christhekeele

christhekeele

FWIW these days I register these release-resiliant path concerns in my application’s compile-time config.exs. I find it to be more reliable and easier to reason about if I do it myself:

# config/config.exs
import Config

root_dir = Path.expand("../..", __DIR__)
priv_dir = Path.expand(Path.join(project_root, "priv"))
assets_dir = Path.expand(Path.join(project_root, "assets"))
static_dir = Path.expand(Path.join(priv_dir, "static"))
static_assets_dir = Path.expand(Path.join(static_dir, "assets"))

config :project, :root_dir, root_dir
config :project, :priv_dir, priv_dir
config :project, :assets_dir, assets_dir
config :project, :static_dir, static_dir
config :project, :static_assets_dir, static_assets_dir

# Use variables above to configure things like `:esbuild` in this file

# Use `Application.compile_env!(:project, :static_assets_dir)`
# to configure things like `Plug.Static`

This works well for umbrella apps, since the each have their own config :app_name space.

For often-referenced paths, I’ll even add a shortcut in my project’s main entrypoint module:

# lib/project/application.ex
defmodule Project.Application do
  def app_name, do: :project
  def root_dir, do: Application.compile_env!(app_name(), :root_dir)
  def static_dir, do: Path.relative_to(Application.compile_env!(app_name(), :static_dir), root_dir())
end

Then, for example, configuring Plug.Static can look like:

# lib/project_web/endpoint.ex
plug Plug.Static,
  at: "/",
  from: {Project.Application.name(), Project.Application.static_dir()}
LostKobrakai

LostKobrakai

Tbh that feels a bit bend over backwards to start with relative paths, expand them to absolute paths for the context of the mix project, just to turn them back into relative paths within the application code again. But I do like the approach of having functions return the paths. I’ve used that approach as well in some projects. It puts a clear interface of what paths are provided by an application / which ones are used by downstream consumers.

Fl4m3Ph03n1x

Fl4m3Ph03n1x

I am generally not comfortable having functions that return configuration settings.

To me, configurations are different from the functionality an application offers, they are a prerequisite and should therefore have their own space, instead of polluting the main API that I expose to the public.

Still, if nothing else works, I will definitely use such approaches, I find code that actually does something better than code that is theoretically more perfect, but then does nothing :stuck_out_tongue:

Also, thanks @christhekeele for your input. I appreciate you took the time to read all the threads and then took even more time to add your own opinion, even though the topic is becoming long and I had marked it as solved.

Where Next?

Popular in Questions Top

Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
logicmason
Hi there, I'm working through my first release with elixir/phoenix. I've built a release with distillery and found that it crashes when I...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
mathew4509
I have a list say x = ["23gh", "56kh", "97mh"] I would like to pass each element to Val in each iteration. Say, in iteration 1 -------...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New

Other popular topics Top

lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

We're in Beta

About us Mission Statement