How to name the generated tarball from an Elixir release?

Background

I am making an elixir release in tarball format:

 defp releases,
    do: [
      my_app: [
        applications: [
          web_interface: :permanent,
          runtime_tools: :permanent
        ],
        steps: [:assemble, :tar],
        include_executables_for: [:windows]
      ]
    ]

The release file has all necessary files, but the name generated is $release_name-$version, meaning it will be something like: my_app-2.1.5.

Problem

This means that every time I release, I need to change shortcuts and other files to have the correct name of the tarball.

Question

Is there a way to give a name to the tarball created that wont change with every release?

This is not answering your question directly but I’ve seen teams just update a symlink – is that not an option? Windows has “junctions” which are practically the same thing.

Consider using the path option for releases to customize where the release is being built.

This is actually the option I am using (using shortcuts). The problem is that the path to the .bat file changes with every release (because the version changes). This is the issue I am trying to minimize.

Junctions would suffer from the same issue as the folders the .bat is in change name with every release.

https://hexdocs.pm/elixir/1.16/config-and-releases.html

I cannot find any option for path: can you help me find it?
Also path will only be the place where the release is built, right? (so I still have the issue of the folders changing names with every release)

If there’s no built-in way to customise the path note you can always add a custom release step. You can grab name and version from the given %Mix.Release{} struct.

- steps: [:assemble, :tar],
+ steps: [:assemble, :tar, &rename_tar/1],

# ...

defp rename_tar(release) do
  # ...
  release
end
5 Likes

I understand I can influence the name of the release, but I don’t see how I can change the name of the .tar, as it is #{name}-#{version}. I can set version: "" but then I end up with a tar called market_manager-.

%Mix.Release{
  name: :market_manager,
  version: "",
  path: "c:/Users/user/Documents/market_manager/_build/prod/rel/market_manager",
  version_path: "c:/Users/user/Documents/market_manager/_build/prod/rel/market_manager/releases/2.1.5",
  applications: %{
    elixir: [
      path: ~c"c:/Users/user/scoop/apps/elixir/current/bin/../lib/elixir",
      otp_app?: false,
      mode: :permanent,
      ...
    ],
    ...
  },
  boot_scripts: %{
    start: [
      kernel: :permanent,
      parse_trans: :permanent,
      phoenix: :permanent,
      phoenix_html: :permanent,
      phoenix_live_view: :permanent,
      ...
    ],
    start_clean: [
      kernel: :permanent,
      phoenix: :none,
      phoenix_html: :none,
      ...
    ]
  },
  erts_source: ~c"c:/Users/user/scoop/apps/erlang/26.1.1/erts-14.1",
  erts_version: ~c"14.1",
  config_providers: [
    {Config.Reader,
     [
       path: {:system, "RELEASE_ROOT", "/releases/2.1.5/runtime.exs"},
       env: :prod,
       target: :host,
       imports: :disabled
     ]}
  ],
  options: [
    reboot_system_after_config: false,
    overwrite: false,
    quiet: false,
    strip_beams: true,
    include_executables_for: [:windows]
  ],
  overlays: [],
  steps: [:tar]
}

I suppose I can cheat the system by updating the name and version of the app before release:

  defp releases,
    do: [
      market_manager: [
        applications: [
          web_interface: :permanent,
          runtime_tools: :permanent
        ],
        steps: [:assemble, &rename_tar/1, :tar],
        include_executables_for: [:windows]
      ]
    ]

  defp rename_tar(release) do
   %{release | version: "data", name: :application}
  end

Which will result in a tar called application-data, but it honestly feels like cheating.

Is there another way to achieve the same without usurping the name and version fields?

After thinking more about this, I now believe there is another way. I can follow @wojtekmach advice to the letter here, allow the steps to be steps: [:assemble, :tar, &rename_tar/1], and then use path (from release argument) to build get the final tar created: #{release.path}/#{name}-#{version}.tar.gz.

Once I have this, I can use File — Elixir v1.12.3 to rename the file and that should work.

This precisely fixes the issue I am fighting with and respects the release data structure!

Yeah I meant file rename. :slight_smile: glad it helped!

1 Like

For future reference, this is what I ended up with:

  defp releases,
    do: [
      market_manager: [
        applications: [
          web_interface: :permanent,
          runtime_tools: :permanent
        ],
        steps: [:assemble, :tar, &rename_tar/1],
        include_executables_for: [:windows]
      ]
    ]

  defp rename_tar(release) do
    tar_folder_path =
      release.path
      |> Path.join("../../")
      |> Path.expand()

    tar_path = Path.join(tar_folder_path, "#{release.name}-#{release.version}.tar.gz")
    new_tar_path = Path.join(tar_folder_path, "application-data.tar.gz")

    case File.rename(tar_path, new_tar_path) do
      :ok -> release
      err -> err
    end
  end

It works like a charm :smiley:

2 Likes