defmodule Mix.Tasks.Publish do @shortdoc "Build a docker image, tag it with app name and version, and publish it" use Mix.Task def run(_args) do # NOTE: This function really needs some refactoring once the functionality is deemed to be OK! cfg = Mix.Project.config version = calculate_version app = cfg[:app] docker_repo = cfg[:docker_repo] docker_repo != nil or raise ":docker_repo must be set in mix.exs. This is to protect against accidentally publishing in public repos" tag = "#{docker_repo}/#{app}:#{version}" cwd = System.cwd elixir_version = System.version {_, 0} = System.cmd("rm", ["-rf", "rel/#{app}"], into: IO.stream(:stdio, :line)) # To be sure, but not doing this also seems to break the release config reading # as the app spec can't be found. Mix.Task.run("compile") release_config = Mix.Releases.Config.read!("rel/config.exs") environments_to_build = release_config.environments |> Map.keys # Build the deployment tarballs with distillery under Docker Enum.map(environments_to_build, fn(env) -> build_args = ["run", "-v", "#{cwd}:#{cwd}", "-e", "MIX_ENV=#{env}", "msaraiva/elixir-dev:#{elixir_version}", "sh", "-c", "cd #{cwd}; set -ev; mix do deps.get --only=#{env}, release"] {_, 0} = System.cmd("docker", build_args, into: IO.stream(:stdio, :line)) end) # Get the tarballs we just built and feed them and some assorted # stuff to the Dockerfile template. release_path = "rel/#{app}/releases/" base_version = String.replace(cfg[:version], "-#{Mix.env}", "") {:ok, entries} = File.ls(release_path) release_tars = entries |> Enum.filter(fn(e) -> String.starts_with?(e, base_version) end) |> Enum.map(fn(e) -> release_path <> e <> "/#{app}.tar.gz" end) add_dist_tars = release_tars |> Enum.map(fn(t) -> "ADD #{t} #{app}" end) |> Enum.join("\n") cmd_for_mix_env = "/#{app}/releases/#{base_version}-${MIX_ENV}/#{app}.sh" docker_file = EEx.eval_file(System.cwd <> "/Dockerfile.deploy.eex", [ add_dist_tars: add_dist_tars, cmd_for_mix_env: cmd_for_mix_env]) File.write!("Dockerfile", docker_file) # Build/tag and push the deployment docker file {_, 0} = System.cmd("docker", ["build", "-t", tag, "."], into: IO.stream(:stdio, :line)) {_, 0} = System.cmd("docker", ["push", tag], into: IO.stream(:stdio, :line)) end @spec calculate_version :: String.t def calculate_version do {rev, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"]) String.replace_trailing(rev, "\n", "") end end