mirror of
https://github.com/ash-project/ash_postgres.git
synced 2024-09-20 13:33:50 +12:00
117 lines
3.6 KiB
Elixir
117 lines
3.6 KiB
Elixir
defmodule Mix.Tasks.AshPostgres.SquashSnapshots do
|
|
use Mix.Task
|
|
|
|
@shortdoc "Cleans snapshots folder, leaving only one snapshot per resource"
|
|
|
|
@switches [
|
|
into: :string,
|
|
snapshot_path: :string,
|
|
quiet: :boolean,
|
|
dry_run: :boolean,
|
|
check: :boolean
|
|
]
|
|
|
|
@moduledoc """
|
|
Cleans snapshots folder, leaving only one snapshot per resource.
|
|
|
|
## Examples
|
|
|
|
mix ash_postgres.squash_snapshots
|
|
mix ash_postgres.squash_snapshots --check --quiet
|
|
mix ash_postgres.squash_snapshots --into zero
|
|
mix ash_postgres.squash_snapshots --dry-run
|
|
|
|
## Command line options
|
|
|
|
* `--into` -
|
|
`last`, `first` or `zero`. The default is `last`. Determines which name to use for
|
|
a remaining snapshot. `last` keeps the name of the last snapshot, `first` renames it to the previously first,
|
|
`zero` sets name with fourteen zeros.
|
|
* `--snapshot-path` - a custom path to stored snapshots. The default is "priv/resource_snapshots".
|
|
* `--quiet` - no messages will not be printed.
|
|
* `--dry-run` - no files are touched, instead prints folders that have snapshots to squash.
|
|
* `--check` - no files are touched, instead returns an exit(1) code if there are snapshots to squash.
|
|
"""
|
|
|
|
def run(args) do
|
|
{opts, []} = OptionParser.parse!(args, strict: @switches)
|
|
|
|
opts =
|
|
opts
|
|
|> Map.new()
|
|
|> Map.put_new(:into, "last")
|
|
|> Map.put_new(:snapshot_path, "priv/resource_snapshots")
|
|
|> Map.put_new(:quiet, false)
|
|
|> Map.put_new(:dry_run, false)
|
|
|> Map.put_new(:check, false)
|
|
|> Map.update!(:into, fn
|
|
"last" -> :last
|
|
"first" -> :first
|
|
"zero" -> :zero
|
|
_other -> raise "Valid values for --into flag are `last`, `first` and `zero`."
|
|
end)
|
|
|
|
squashable =
|
|
opts.snapshot_path
|
|
|> Path.join("**/*.json")
|
|
|> Path.wildcard()
|
|
|> Enum.filter(&String.match?(Path.basename(&1), ~r/^\d{14}\.json$/))
|
|
|> Enum.group_by(&Path.dirname(&1))
|
|
|> Enum.reduce([], fn {folder, snapshots}, squashable ->
|
|
last_snapshot = Enum.max(snapshots)
|
|
|
|
into_snapshot =
|
|
case opts.into do
|
|
:last -> last_snapshot
|
|
:first -> Enum.min(snapshots)
|
|
:zero -> Path.join(folder, "00000000000000.json")
|
|
end
|
|
|
|
if length(snapshots) > 1 or last_snapshot != into_snapshot do
|
|
[{folder, snapshots, last_snapshot, into_snapshot} | squashable]
|
|
else
|
|
squashable
|
|
end
|
|
end)
|
|
|> Enum.reverse()
|
|
|
|
if Enum.empty?(squashable) do
|
|
print(opts, "No snapshots to squash.")
|
|
else
|
|
if opts.dry_run do
|
|
print(opts, "Snapshots in following folders would have been squashed in non-dry run:")
|
|
print(opts, Enum.map_join(squashable, "\n", fn {folder, _, _, _} -> folder end))
|
|
|
|
if opts.check do
|
|
exit({:shutdown, 1})
|
|
end
|
|
end
|
|
|
|
if opts.check do
|
|
print(opts, """
|
|
Snapshots would have been squashed, but the --check flag was provided.
|
|
|
|
To see what snapshots would have been squashed, run with the --dry-run
|
|
flag. To squash those snapshots, run without either flag.
|
|
""")
|
|
|
|
exit({:shutdown, 1})
|
|
end
|
|
|
|
if not opts.dry_run do
|
|
for {_folder, snapshots, last_snapshot, into_snapshot} <- squashable do
|
|
for snapshot <- snapshots, snapshot != last_snapshot do
|
|
File.rm!(snapshot)
|
|
end
|
|
|
|
if last_snapshot != into_snapshot do
|
|
File.rename!(last_snapshot, into_snapshot)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
defp print(%{quiet: true}, _message), do: nil
|
|
defp print(_opts, message), do: Mix.shell().info(message)
|
|
end
|