Skip to main content

lib/mix/tasks/release_kit.artifact.ex

defmodule Mix.Tasks.ReleaseKit.Artifact do
  @moduledoc """
  Builds an OTP release tarball and ReleaseKit ETF manifest.

      MIX_ENV=prod mix release_kit.artifact --out-dir _build/prod/artifacts

  The task intentionally stops at producing files. Deployment tools are
  responsible for unpacking, configuring, and supervising the release.

  Existing artifacts from a clean checkout are reused by default. Pass
  `--force` to rebuild even when the current manifest and tarball match the
  checkout.
  """

  use Mix.Task

  alias ReleaseKit.Builder

  @shortdoc "Builds an OTP release artifact and manifest"

  @impl true
  def run(args) do
    {opts, rest, invalid} =
      OptionParser.parse(args,
        strict: [
          out_dir: :string,
          release: :string,
          version: :string,
          port: :integer,
          health_path: :string,
          skip_release: :boolean,
          force: :boolean,
          target_os: :string,
          target_arch: :string,
          target_libc: :string,
          target_suffix: :boolean
        ]
      )

    if invalid != [], do: Mix.raise("Invalid options: #{inspect(invalid)}")
    if rest != [], do: Mix.raise("Unexpected arguments: #{inspect(rest)}")

    result =
      Builder.build(
        out_dir: Keyword.get(opts, :out_dir),
        release: Keyword.get(opts, :release),
        version: Keyword.get(opts, :version),
        port: Keyword.get(opts, :port),
        health_path: Keyword.get(opts, :health_path),
        force?: Keyword.get(opts, :force, false),
        target: target(opts),
        target_suffix?: Keyword.get(opts, :target_suffix, false),
        build_release?: not Keyword.get(opts, :skip_release, false)
      )

    Mix.shell().info("Release tarball: #{result.tarball}")
    Mix.shell().info("ReleaseKit manifest: #{result.manifest_path}")
  end

  defp target(opts) do
    target = [
      os: Keyword.get(opts, :target_os),
      arch: Keyword.get(opts, :target_arch),
      libc: Keyword.get(opts, :target_libc)
    ]

    target = Enum.reject(target, fn {_key, value} -> is_nil(value) end)
    if target == [], do: nil, else: target
  end
end