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 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" elixir_version = System.version build_image = cfg[:build_image] || "msaraiva/elixir-dev:#{elixir_version}" tag = "#{docker_repo}/#{app}:#{calculate_version(args)}" cwd = System.cwd {_, 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. # We need to make sure that we clean out native stuff in case they # transfer from host to target system. Enum.map(environments_to_build, fn(env) -> build_args = ["run", "-v", "#{cwd}:#{cwd}", "-e", "MIX_ENV=#{env}", build_image, "sh", "-c", "cd #{cwd}; set -evx; mix deps.get; find . -name '*.so' -o -name '*.o' | xargs rm -f; mix do deps.compile, release; echo -e '\\0033\\0143'"] {_, 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 = "ADD " <> Enum.join(release_tars, " ") <> " #{app}/" 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]) :: String.t def calculate_version(args) do {opts, _, _ } = OptionParser.parse( args, switches: [ tag_version: :string, ] ) case opts[:tag_version] do nil -> {rev, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"]) String.replace_trailing(rev, "\n", "") v -> v end end end