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 cfg = Mix.Project.config app = cfg[:app] version = cfg[:version] 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_dockerfile = cfg[:build_dockerfile] || nil path_in_container = cfg[:path_in_container] || "/opt/app/" hex_dir = System.get_env("HOME") <> "/.hex" if build_dockerfile != nil and cfg[:build_image] != nil do raise "Only specify one of 'build_dockerfile' or 'build_image'" end # Optionally allow users to use a local dockerfile as the build_image build_image = if build_dockerfile do build_image_tag = "docker_distiller_build_image" {_, 0} = System.cmd("docker", ["build", "-t", build_image_tag, "-f", build_dockerfile, "."], into: IO.stream(:stdio, :line)) build_image_tag else cfg[:build_image] || "bitwalker/alpine-elixir:#{elixir_version}" end tag = "#{docker_repo}/#{app}:#{calculate_version(args)}" cwd = System.cwd run_cmd("rm", ["-rf", "deps", "_build"]) run_cmd("mix", ["deps.get"]) build_args = ["run", "-v", "#{cwd}:#{cwd}", "-v", "#{hex_dir}:/root/.hex", "-e", "MIX_ENV=deploy", build_image, "sh", "-c", "cd #{cwd}; set -evx; find . -name '*.so' -o -name '*.o' | xargs rm -f; mix compile; mix release"] run_cmd("docker", build_args) # Get the tarball we just built and feed it and some assorted # stuff to the Dockerfile template. dist_tar = "_build/deploy/rel/#{app}/releases/#{version}/#{app}.tar.gz" add_dist_tar = "ADD #{dist_tar} #{path_in_container}#{app}/" cmd_to_run = "#{path_in_container}#{app}/bin/#{app}" docker_file = EEx.eval_file(System.cwd <> "/Dockerfile.deploy.eex", [ add_dist_tar: add_dist_tar, cmd_to_run: cmd_to_run]) File.write!("Dockerfile", docker_file) # Build/tag and push the deployment docker file run_cmd("docker", ["build", "-t", tag, "."]) run_cmd("docker", ["push", tag]) 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 def run_cmd(cmd, args) do IO.puts("Running: #{cmd} with #{inspect args}") {_, 0} = System.cmd(cmd, args, into: IO.stream(:stdio, :line)) end end