lib/mix/tasks/sr_service.new.ex

defmodule Mix.Tasks.SrService.New do
  @moduledoc """
  Used to generate a new service for use in the SalesRabbit ecosystem.
  """
  use Mix.Task

  @app_name_replace_text "<APP_NAME>"
  @module_name_replace_text "<MODULE_NAME>"

  @impl true
  def run(args) do
    case OptionParser.parse(args, switches: []) do
      {opts, [name], _} ->
        name = String.downcase(name)
        Mix.shell().info("Generating '#{name}' with options #{inspect(opts)}")
        generate(name, opts)

      _ ->
        Mix.raise(
          "You need to pass in a name and valid options! (example: mix sr_service new_app --db)"
        )
    end
  end

  defp generate(app_name, []) do
    {:ok, app_dir} = create_base_app(app_name)
    :ok = copy_base_files(app_dir, app_name)
    SrService.write_log_file(app_dir, app_name)
    SrService.update_application_file(app_dir, app_name)
    SrService.update_mix_file(app_dir)
    SrService.write_repo_file(app_dir, app_name)
    replace_app_name(app_dir, app_name)
    format_files(app_dir)
    install_deps(app_dir)
    print_mix_info()
  end

  defp create_base_app(name) do
    pwd = File.cwd!()
    app_dir = "#{pwd}/#{name}"

    :ok = Mix.Task.run("new", ["#{app_dir}", "--sup"])
    {:ok, app_dir}
  end

  defp copy_base_files(app_dir, app_name) do
    File.cp!("#{template_dir()}/template.README.md", "#{app_dir}/README.md",
      on_conflict: fn _, _ -> true end
    )

    File.cp!("#{template_dir()}/template.service.ex", "#{app_dir}/lib/#{app_name}.ex",
      on_conflict: fn _, _ -> true end
    )

    File.cp!(
      "#{template_dir()}/template.service_test.exs",
      "#{app_dir}/test/#{app_name}_test.exs",
      on_conflict: fn _, _ -> true end
    )

    File.cp!("#{template_dir()}/template.tool-versions", "#{app_dir}/.tool-versions",
      on_conflict: fn _, _ -> true end
    )

    File.cp!("#{template_dir()}/template.gitignore", "#{app_dir}/.gitignore",
      on_conflict: fn _, _ -> true end
    )

    File.cp!("#{template_dir()}/template.coveralls.json", "#{app_dir}/coveralls.json",
      on_conflict: fn _, _ -> true end
    )

    File.cp!("#{template_dir()}/template.Dockerfile", "#{app_dir}/Dockerfile",
      on_conflict: fn _, _ -> true end
    )

    File.cp!("#{template_dir()}/template.credo.exs", "#{app_dir}/.credo.exs",
      on_conflict: fn _, _ -> true end
    )

    File.cp!("#{template_dir()}/template.gitlab-ci.yml", "#{app_dir}/.gitlab-ci.yml",
      on_conflict: fn _, _ -> true end
    )

    File.mkdir_p!("#{app_dir}/lib/mix/tasks/")
    File.mkdir_p!("#{app_dir}/sdk")
    File.touch("#{app_dir}/sdk/.gitkeep")

    File.cp!("#{template_dir()}/template.build_sdk.ex", "#{app_dir}/lib/mix/tasks/build_sdk.ex",
      on_conflict: fn _, _ -> true end
    )

    # CONFIG FILES
    File.mkdir!("#{app_dir}/config")

    File.cp!("#{template_dir()}/template.config.exs", "#{app_dir}/config/config.exs",
      on_conflict: fn _, _ -> true end
    )

    File.cp!("#{template_dir()}/template.runtime.exs", "#{app_dir}/config/runtime.exs",
      on_conflict: fn _, _ -> true end
    )

    File.cp!("#{template_dir()}/template.env", "#{app_dir}/config/.env",
      on_conflict: fn _, _ -> true end
    )

    # RELEASE FILES
    File.mkdir!("#{app_dir}/rel")

    File.cp!("#{template_dir()}/template.env.sh.eex", "#{app_dir}/rel/env.sh.eex",
      on_conflict: fn _, _ -> true end
    )

    :ok
  end

  defp template_dir do
    "#{:code.priv_dir(:sr_service)}"
  end

  defp replace_app_name(app_dir, app_name) do
    files =
      ~w(#{app_dir}/lib/#{app_name}.ex #{app_dir}/test/#{app_name}_test.exs #{app_dir}/.gitlab-ci.yml #{app_dir}/Dockerfile #{app_dir}/README.md #{app_dir}/mix.exs #{app_dir}/config/runtime.exs #{app_dir}/coveralls.json)

    Enum.each(files, fn filename ->
      contents =
        File.read!(filename)
        |> String.replace(@app_name_replace_text, app_name)
        |> String.replace(@module_name_replace_text, SrService.app_module_name(app_name))

      File.write!(filename, contents)
    end)
  end

  defp format_files(app_dir) do
    Mix.Task.run("format", ["#{app_dir}/**/*.{ex,exs}"])
  end

  defp install_deps(app_dir) do
    File.cd!(app_dir)
    System.cmd("mix", ["deps.get"], env: [{"DATABASE_URL", "nothing"}])
  end

  defp print_mix_info do
    Mix.shell().info("""

    To Start your new app run:
    $ iex -S mix

    Some Followup items may include:
      - Setting up your project in Gitlab
      - Working with the infrastructure team to get your new app deployed
    """)
  end
end