Current section

Files

Jump to
rodar_release lib mix tasks rodar_release.rollback.ex
Raw

lib/mix/tasks/rodar_release.rollback.ex

defmodule Mix.Tasks.RodarRelease.Rollback do
@shortdoc "Undo the last release"
@moduledoc """
Undoes the last release by deleting its tag and resetting the release commit.
mix rodar_release.rollback # soft reset (keep changes staged)
mix rodar_release.rollback --hard # discard all release changes
mix rodar_release.rollback --dry-run # preview rollback
Requires the latest commit to be a release commit (`release: vX.Y.Z`).
## Modes
* **Soft (default)** - resets the commit and restores `mix.exs` and `CHANGELOG.md`
to the pre-release state.
* **Hard (`--hard`)** - discards all release changes entirely.
## Options
* `--dry-run` - show what would happen without making any changes
* `--hard` - discard release changes entirely
"""
use Mix.Task
import RodarRelease.Helpers
@impl Mix.Task
def run(args) do
{opts, _, _} = OptionParser.parse(args, strict: [dry_run: :boolean, hard: :boolean])
dry_run = Keyword.get(opts, :dry_run, false)
hard = Keyword.get(opts, :hard, false)
{release_version, commit_msg} = get_release_commit!()
tag = "v#{release_version}"
Mix.shell().info("Rollback plan:")
Mix.shell().info(" Release to rollback: #{tag}")
Mix.shell().info(
" Reset mode: #{if hard, do: "hard (discard changes)", else: "soft (keep changes staged)"}"
)
Mix.shell().info("")
if dry_run do
Mix.shell().info("[dry-run] Would delete tag #{tag}")
Mix.shell().info("[dry-run] Would reset commit: #{commit_msg}")
if hard do
Mix.shell().info("[dry-run] Would discard all release changes")
else
Mix.shell().info("[dry-run] Would keep release changes staged")
end
else
step("Deleting tag #{tag}", fn ->
git!(["tag", "-d", tag])
end)
reset_mode = if hard, do: "--hard", else: "--soft"
step("Resetting release commit (#{reset_mode})", fn ->
git!(["reset", reset_mode, "HEAD~1"])
end)
if hard do
Mix.shell().info("")
Mix.shell().info("Rollback complete! Release #{tag} has been fully undone.")
else
step("Restoring mix.exs and CHANGELOG.md from pre-release state", fn ->
git!(["checkout", "HEAD", "--", "mix.exs", "CHANGELOG.md"])
end)
Mix.shell().info("")
Mix.shell().info("Rollback complete! Release #{tag} has been undone.")
Mix.shell().info("The working tree is clean and back to the pre-release state.")
end
end
end
end