Umbrella project with symbolic links to apps possible?

I have multiple umbrella apps which use the same apps. To have no copies of those apps, I tried to place them in a directory and use symbolic links like in the minimal example below (“project1” is a fresh umbrella project):

$ tree .
.
├── project1
│   ├── apps
│   │   ├── app1 -> ../../shared_apps/app1/
│   │   └── app2 -> ../../shared_apps/app2
│   ├── config
│   │   └── config.exs
│   ├── mix.exs
│   └── README.md
└── shared_apps
    ├── app1
    │   ├── lib
    │   │   └── app1.ex
    │   ├── mix.exs
    │   ├── README.md
    │   └── test
    │       ├── app1_test.exs
    │       └── test_helper.exs
    └── app2
        ├── lib
        │   └── app2.ex
        ├── mix.exs
        ├── README.md
        └── test
            ├── app2_test.exs
            └── test_helper.exs

12 directories, 13 files

But when app2 depends on app1 via in_umbrella: true, like below

$ cat shared_apps/app2/mix.exs 
defmodule App2.MixProject do
  use Mix.Project

  def project do
    [
      app: :app2,
      version: "0.1.0",
      elixir: "~> 1.9",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  # Run "mix help compile.app" to learn about applications.
  def application do
    [
      extra_applications: [:logger]
    ]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      {:app1, in_umbrella: true}
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
    ]
  end
end

then I get error messages:

$ cd project1/ && mix deps.get
Dependencies have diverged:
* app1 (apps/app1)
  the dependency app1 in mix.exs is overriding a child dependency:

  > In mix.exs:
    {:app1, [path: "apps/app1", from_umbrella: true, env: :dev]}

  > In /home/peter/tmp/testapps/shared_apps/app2/mix.exs:
    {:app1, [env: :dev, path: "../app1", in_umbrella: true]}

  Please remove the conflicting options from your definition
** (Mix) Can't continue due to errors on dependencies

How can I fix this?

hmm this is not really what you are asking, but have you considered having those apps as dependencies fetched from git?

1 Like

This is a possibility, thank you! But is it also possible with symbolic links?

I now use bindfs instead of symbolic links to avoid copies of the same code (shared apps). This seems to work.
But it would still nice to know if it can’t be done with symbolic links…

I don’t know if it is possible but how would you handle it if you have to work on project1 from different machines or with a team?

Hex.pm, or hex private if you’re willing to pay a small monthly fee.

1 Like

TBH it sounds more like you have one umbrella app that several artifacts are created from. It’s possible to package just some applications from the umbrella into a release, so you could have one release that contained project_1, app1, app2 and another that contained project_2, app1, app2.

If project_1 and project_2 are so dissimilar that they shouldn’t be in one umbrella, definitely consider a more-structured way of controlling when changes from app1 and app2 affect each project (via Hex packaging or similar); what happens in the symlink approach when a change to app1 to support new code in project_1 breaks project_2?

2 Likes

Sure, I know… My question is rather about OP concern.

Yes, it sounds correct to you… The shared apps evolve and several artifacts use some of them. The linked solution sounds interesting. Thanks.

The changes in app1 for example will most probably not break other projects in my case but true, in general, this would be a concern.

To handle different machines, I thougt about having a git repository contining all the apps (which is not an umbrella project) and other git repositories for each project that uses them. To release one I thought it would be easy to just clone the apps repository and one project repo, then set the symbolic link and generate the release.
If it was possible, what would you think about this? (I think the multiple artifacts proposal of al2o3cr is appropriate for me)

1 Like

At this point with your helpful proposal, it seems convenient to not only package just some applications into a release but also to be able to start just some applications when I invoke iex -S mix (before I build a release).
Do you know of any convenient way of specifying that only some application and its dependencies (instead of all apps that are located in the apps directory of an umbrella project) shall be started when I do iex -S mix?

If you only want to start one app within the umbrella, then cd into the app, cd apps/foo and do iex -S mix or possibly iex -S mix phx.server. That will start only that app and any of it’s dependencies.

This is also a good way to verify that you have the apps separated the way you want. You should only be able to see the modules of other apps in iex that are listed as a dependency in the started app’s mix file, i.e. {:bar, in_umbrella: true}.

1 Like

Thank you! Exactly what I was looking for.