Current section

Files

Jump to
patch lib mix tasks patch.release.ex
Raw

lib/mix/tasks/patch.release.ex

defmodule Mix.Tasks.Patch.Release do
@moduledoc """
Prepares Patch for the next release.
It is expected that the version will be updated in mix.exs **before** running
this tool.
This tool will collect some necessary information for the changelog.
There are some minor differences in how Github and ExDocs want to link and
render things. This task also takes care of generating custom markdown that
renders properly in Github and ExDocs. Github markdown files end up in the
root of the project, the ExDocs markdown files end up in /pages and are
included via extras in mix.exs.
"""
@shortdoc "Prepares Patch for the next release."
use Mix.Task
@exdocs_links %{
assert_refute_calls: "#asserting-refuting-calls",
changelog: "changelog.html"
}
@github_links %{
assert_refute_calls: "#asserting--refuting-calls",
changelog: "CHANGELOG.md"
}
def run(_) do
config = Mix.Project.config()
version = config[:version]
releases =
"./pages/templates/releases.etf"
|> Path.expand()
|> File.read!()
|> :erlang.binary_to_term()
[latest_release | previous_releases] = releases
Mix.shell.info(["Preparing release of patch at version ", :cyan, version, :default_color])
releases =
if latest_release.version == version do
if confirm?([:yellow, "WARNING ", :default_color, "Version ", :cyan, version, :default_color, " is already defined. Overwrite?"]) do
prepare_release(version, previous_releases)
else
releases
end
else
prepare_release(version, releases)
end
render_files(version, releases)
end
def prepare_release(version, releases) do
date = get_date()
improvements = entries("Improvements")
features = entries("Features")
bugfixes = entries("Bugfixes")
deprecations = entries("Deprecations")
removals = entries("Removals")
release = %{
version: version,
date: date,
improvements: improvements,
features: features,
bugfixes: bugfixes,
deprecations: deprecations,
removals: removals
}
[release | releases]
end
def render_files(version, releases) do
releases_binary = :erlang.term_to_binary(releases)
readme_template = Path.expand("./pages/templates/README.eex")
changelog_template = Path.expand("./pages/templates/CHANGELOG.eex")
releases_path = Path.expand("./pages/templates/releases.etf")
exdocs_changelog_path = Path.expand("./pages/CHANGELOG.md")
github_changelog_path = Path.expand("./CHANGELOG.md")
exdocs_readme_path = Path.expand("./pages/README.md")
github_readme_path = Path.expand("./README.md")
exdocs_changelog = EEx.eval_file(changelog_template, [releases: releases])
github_changelog = EEx.eval_file(changelog_template, [releases: releases])
exdocs_readme = EEx.eval_file(readme_template, [version: version, links: @exdocs_links])
github_readme = EEx.eval_file(readme_template, [version: version, links: @github_links])
File.write!(exdocs_changelog_path, exdocs_changelog)
File.write!(github_changelog_path, github_changelog)
File.write!(exdocs_readme_path, exdocs_readme)
File.write!(github_readme_path, github_readme)
File.write!(releases_path, releases_binary)
info(["Version ", :cyan, version, :default_color, " has been ", :green, "successfully", :default_color, " prepared for release. 🚀"])
end
def get_date do
today =
Date.utc_today()
|> Date.to_string()
case prompt(["Date ", :cyan, today, :default_color, " (leave blank to use, or provide a custom date)"]) do
"" ->
today
date ->
date
end
end
def entries(prompt, acc \\ []) do
case prompt([:cyan, prompt, :default_color, " (leave blank when done)"]) do
"" ->
acc
entry ->
entries(prompt, [entry | acc])
end
end
def info(message) do
Mix.shell.info(message)
end
def prompt(prompt) do
prompt
|> IO.ANSI.format()
|> IO.iodata_to_binary()
|> Mix.shell.prompt()
|> String.trim()
end
def confirm?(prompt) do
prompt
|> IO.ANSI.format()
|> IO.iodata_to_binary()
|> Mix.shell.yes?()
end
end