对 elixir 应用程序 phoenix 进行自动版本控制的最佳方法

Best approach to do auto versioning of an elixir application phoenix

我正在使用 distillery 为 phoenix 应用程序进行热代码升级。我用它来创建应用程序的版本。

  def project do
    {result, _exit_code} = System.cmd("git", ["rev-parse", "HEAD"])
    git_sha = String.slice(result, 0, 7)

    [app: :evercam_media,
     version: "1.0.1-a#{git_sha}",
     elixir: "~> 1.7",
     elixirc_paths: elixirc_paths(Mix.env),
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     compilers: [:phoenix] ++ Mix.compilers,
     aliases: aliases(),
     deps: deps()]
  end

这会创建版本,但该版本不适用于热代码升级并会弄乱 relup 文件。当我进行 1.0.1、1.0.2 和 1.0.3 之类的版本控制时。然后热代码部署工作正常。但是有什么办法可以让这个东西动态化吗?

我正在使用 distillery 部署到远程服务器。

更新:

我正在使用 distillery 进行部署,但自动版本无法正常工作,除非它们是 1.0.1 或 1.0.2 或 1.0.3。我试过下面的答案。以及 git describe --tags 也是如此。但酒厂只使用 2 个版本,然后每次使用过去的版本创建新版本。

git_sha 的问题是它没有排序。根据普通字母数字排序,下一个版本的编号被假定为大于前一个版本。 sha 情况并非如此。

您可以为此目的使用日期。例如

git log -1 --date=raw --format=%cd
#⇒ 1535467693 +0200 # seconds since epoch

现在让我们把它炼成:

{epoch, _} = System.cmd("git", ~w|log -1 --date=raw --format=%cd|)
[sec, tz] =
  epoch
  |> String.split(~r/\s+/, trim: true)
  |> Enum.map(&String.to_integer/1)
#⇒ [1527769224, 200]
sec + tz * 36 # * 60 * 60 / 100
#⇒ 1527776424

上面的数字一直在增长。


旁注: 尽可能使用就地二进制模式匹配而不是 String.slice/3

{<<git_sha::binary-size(8), _rest::binary>>, _exit_code} =
  System.cmd("git", ["rev-parse", "HEAD"])
#⇒ {"556c53987eb55c82ffb6925f9f56eae5de01c119\n", 0}
git_sha
#⇒ "556c5398"

无论您需要的只是让送货愉快,您都可以使用 delivery config 来达到这个目的。只需将以下行放入 .deliver/config:

AUTO_VERSION=commit-count+git-revision+branch-unless-master

这是我用于最终构建为版本化生产 RPM 的应用程序的方法。当针对干净标签构建时,版本是标签名称。否则,该版本包括短提交哈希和自上一个标记以来的提交计数。

它还支持通过在项目的根目录中包含一个 VERSION 文件来覆盖自动版本控制。

# mix.exs
defmodule MyApp.Mixfile do
  use Mix.Project
  @default_version "v1.0.0-default"

  def project do
    [
      app: :my_app,
      version: version(),
      elixir: "~> 1.6",
      # ...
    ]
  end

  # ...

  defp version do
    # Build the version number from Git.
    # It will be something like 1.0.0-beta1 when built against a tag, and
    # 1.0.0-beta1+18.ga9f2f1ee when built against something after a tag.
    with {:ok, string} <- get_version(),
         [_, version, commit] <- Regex.run(~r/(v[\d\.]+(?:\-[a-zA-Z]+\d*)?)(.*)/, String.trim(string)) do
      String.replace(version, ~r/^v/, "") <> (commit |> String.replace(~r/^-/, "+") |> String.replace("-", "."))
    else
      other ->
        IO.puts("Could not get version. error: #{other}")
        @default_version
    end
  end

  defp get_version do
    case File.read("VERSION") do
      {:error, _} ->
        case System.cmd("git", ["describe"]) do
          {string, 0} -> {:ok, string}
          {error, errno} -> {:error, "Could not get version. errno: #{inspect errno}, error: #{inspect error}"}
        end
      ok -> ok
    end
  end
end

我们使用的解决方案是

defmodule EvercamMedia.Mixfile do
  use Mix.Project

  def project do
    [app: :evercam_media,
     version: "1.0.#{DateTime.to_unix(DateTime.utc_now())}",
     elixir: "~> 1.11.1",
     elixirc_paths: elixirc_paths(Mix.env),
     build_embedded: Mix.env == :prod,

这个,它工作得很好。

解决方案,我已经接受了,当你部署分支然后主控时,热升级将不起作用。

此外,使用 Ansible 进行部署时最重要的部分是触摸您的 mix.exs 以便它可以编译并获取新版本。

我已经写了一篇关于它的文章Hot Code Upgrade