Discovers the schema version of every installed PhoenixKit module that owns its migrations.
Modules that ship their own tables implement PhoenixKit.Module.migration_module/0,
returning a coordinator with current_version/0 (what the code needs) and
migrated_version_runtime/1 (what the database has). Examples in the wild:
phoenix_kit_inbox, phoenix_kit_boards, phoenix_kit_web_analytics,
phoenix_kit_legal, phoenix_kit_stats.
This module is the shared read side of that contract. mix phoenix_kit.status
uses it to report per-module versions and mix phoenix_kit.update uses it
to decide what to migrate — before this existed each task had its own copy
of the discovery logic, and only update had it at all, which is why
status never mentioned modules.
Everything here is read-only and defensive: a module whose coordinator raises,
exits, or reports a non-integer version is recorded as :error with the
message, never crashing the caller. A broken third-party module must not take
down mix phoenix_kit.status.
Example
iex> PhoenixKit.Migrations.Modules.list()
[
%{
name: "Inbox",
module: PhoenixKitInbox,
migration_module: PhoenixKitInbox.Migrations,
installed: 1,
target: 1,
status: :up_to_date,
error: nil
}
]
Summary
Functions
Filters a list/1 result down to entries whose database is newer than the
code now running — a rollback, or a dependency pinned backwards. Surfaced
separately from pending/1 so callers don't fold a genuine problem into
"nothing to do" just because it isn't something mix phoenix_kit.update
would act on.
Classifies one module's installed version against the version its code wants.
Filters a list/1 result down to entries whose coordinator raised. Surfaced
separately so tasks can warn about a broken module instead of quietly
omitting it from the report.
Every discovered module that owns migrations, sorted by display name.
Filters a list/1 result down to the entries a mix phoenix_kit.update run
would act on — those needing their tables created or upgraded.
Types
@type entry() :: %{ name: String.t(), module: module(), migration_module: module(), installed: non_neg_integer(), target: non_neg_integer() | nil, status: status(), error: String.t() | nil }
@type status() ::
:not_installed | :needs_update | :up_to_date | :ahead_of_code | :error
Where one module's schema stands.
:not_installed— the code is present but its tables have never been created (installed version 0). The nextmix phoenix_kit.updatecreates them.:needs_update— tables exist at an older version than the code expects.:up_to_date— database matches exactly what the code needs.:ahead_of_code— database is NEWER than what the code expects: the schema was migrated by a later release than the one now running (a rollback/downgrade, or a dependency pinned backwards). Deliberately not folded into:up_to_date— that would hide the downgrade from callers that branch on this value, exactly the failure this type exists to surface.:error— the module's coordinator raised, exited, or reported a non-integer version; the:errorfield has the message.
Functions
Filters a list/1 result down to entries whose database is newer than the
code now running — a rollback, or a dependency pinned backwards. Surfaced
separately from pending/1 so callers don't fold a genuine problem into
"nothing to do" just because it isn't something mix phoenix_kit.update
would act on.
Pure filter, same reasoning as pending/1.
Classifies one module's installed version against the version its code wants.
Public because it is the whole read-side decision — mix phoenix_kit.status
renders it and mix phoenix_kit.update migrates off it — and a private
version could only be tested through a hand-built entry that supplied the
answer, which is no test at all.
Both versions must be integers. A coordinator that reports anything else
(nil for "no version comment found" is the tempting one) is :error, never
silently :up_to_date: under Erlang term ordering nil >= 2 is true, so an
unguarded comparison would mark a module with no tables as current and skip
its migration forever.
installed > target is :ahead_of_code, never :up_to_date — collapsing
the two would silently hide a database that is newer than the code now
running (a downgrade) behind the same status a healthy install reports.
iex> alias PhoenixKit.Migrations.Modules
iex> {Modules.classify(2, 2), Modules.classify(3, 2)}
{:up_to_date, :ahead_of_code}
iex> {Modules.classify(0, 1), Modules.classify(1, 5)}
{:not_installed, :needs_update}
iex> Modules.classify(nil, 2)
:error
Filters a list/1 result down to entries whose coordinator raised. Surfaced
separately so tasks can warn about a broken module instead of quietly
omitting it from the report.
Pure filter, same reasoning as pending/1.
Every discovered module that owns migrations, sorted by display name.
Returns [] when module discovery itself fails (no beam files, app not
loaded) rather than raising — callers are CLI tasks that should degrade to
"no modules" instead of blowing up.
Options
:prefix— Postgres schema the install lives in. Defaults to"public".
Filters a list/1 result down to the entries a mix phoenix_kit.update run
would act on — those needing their tables created or upgraded.
A pure filter, deliberately: an earlier version also accepted options and did
its own list/1, which made pending([]) ambiguous — an empty entry list
and empty options are the same term, so "nothing to filter" silently became
"go query the database". Callers that want both compose them:
Modules.list(prefix: prefix) |> Modules.pending()