PhoenixKit.Migrations.Repair.Report (phoenix_kit v2.5.0)

Copy Markdown View Source

The result shape PhoenixKit.Migrations.Repair.verify/1 and .repair/1 return — versions observed, one finding per object/invariant that was not silently clean, and the derived exit code for mix phoenix_kit.repair (spec §6.1's final pipeline step, §6.2's severity mapping, and the 0/1/2 contract on the mix task's moduledoc).

Fields

  • :prefix — the schema this report is about.
  • :dry_run — whether the run that produced this report was read-only (PhoenixKit.Migrations.Repair.verify/1, or .repair/1 with dry_run: true). Purely descriptive — every finding.kind already disambiguates :would_repair (dry-run) from :repaired (applied).
  • :versions%{comment: comment(), floor: pos_integer(), current: pos_integer()}. comment is the raw value (see PhoenixKit.Migrations.Repair.Probe.raw_comment/2) — :absent (the phoenix_kit table does not exist), nil (table exists, no comment — the half-installed/adopt case), or the numeric value.
  • :comment_action — what, if anything, happened to the version comment this run: :none, {:healed, version} (stale-low, R2/--heal-comment), {:adopted, version} (R4/--adopt), or {:would_heal, version} / {:would_adopt, version} for a dry run that found the condition but did not write.
  • :findings — see finding/0.

Severity → exit code

mix phoenix_kit.repair's contract is exit 0/1/2 (moduledoc); this module computes that from the highest severity present, never from counting kinds one by one, so a run that both fixed something repairable AND surfaced an error-severity divergence reports the divergence's exit code (2) — the operator still needs to see it even though other things got fixed:

  • any severity: :error finding present → 2 ("report-only divergences present" — spec §6.2's error-severity bucket: wrong type/length/ default, unexpected NOT NULL, divergent index/constraint definition, :create_failed, a failing data invariant)
  • else any severity: :repairable finding present → 1 ("repairs applied-or-pending" — covers both an actual :repaired in repair/1 and a :would_repair in verify/1/dry-run, deliberately collapsed to the same code per the mix task moduledoc's own wording)
  • else → 0 (clean; only :info-severity findings, if any)

Summary

Types

Raw comment as read by Probe.raw_comment/2 — never the legacy no-comment→1 mapping.

One reportable event. since/object_id are nil for findings that are not about a single manifest object (comment-policy findings, data invariants use object_id: nil + a since from the invariant).

See the moduledoc's severity → exit code table.

t()

Functions

Appends one finding (prepended internally; findings/1 returns them in append order).

The highest-severity-wins exit code — see the moduledoc's "Severity → exit code" section. 0/1/2 only; never raises.

Findings in the order they were added.

Builds an empty report for the given prefix/dry_run/versions triple.

Sets comment_action.

Counts findings by severity and by kind, plus the total — the numbers mix phoenix_kit.repair's human-readable summary line and --json output both render from.

Plain-data rendering for --json — every value is already a JSON scalar, list, or map except :versions.comment (:absent needs a string form) and :comment_action (a tagged tuple). Both are flattened here; everything else (finding.kind/finding.severity atoms included) is left for the caller's JSON encoder to stringify, matching how mix phoenix_kit.doctor/ release_check already emit atoms as plain output.

Types

comment()

@type comment() :: :absent | nil | non_neg_integer()

Raw comment as read by Probe.raw_comment/2 — never the legacy no-comment→1 mapping.

comment_action()

@type comment_action() ::
  :none
  | {:healed, pos_integer()}
  | {:adopted, pos_integer()}
  | {:would_heal, pos_integer()}
  | {:would_adopt, pos_integer()}

finding()

@type finding() :: %{
  kind: atom(),
  severity: severity(),
  object_id: String.t() | nil,
  since: pos_integer() | nil,
  message: String.t()
}

One reportable event. since/object_id are nil for findings that are not about a single manifest object (comment-policy findings, data invariants use object_id: nil + a since from the invariant).

severity()

@type severity() :: :info | :repairable | :error

See the moduledoc's severity → exit code table.

t()

@type t() :: %PhoenixKit.Migrations.Repair.Report{
  comment_action: comment_action(),
  dry_run: boolean(),
  findings: [finding()],
  prefix: String.t(),
  versions: %{comment: comment(), floor: pos_integer(), current: pos_integer()}
}

Functions

add_finding(report, finding)

@spec add_finding(t(), finding()) :: t()

Appends one finding (prepended internally; findings/1 returns them in append order).

exit_code(report)

@spec exit_code(t()) :: 0 | 1 | 2

The highest-severity-wins exit code — see the moduledoc's "Severity → exit code" section. 0/1/2 only; never raises.

findings(report)

@spec findings(t()) :: [finding()]

Findings in the order they were added.

new(prefix, dry_run, versions)

@spec new(String.t(), boolean(), %{
  comment: comment(),
  floor: pos_integer(),
  current: pos_integer()
}) ::
  t()

Builds an empty report for the given prefix/dry_run/versions triple.

put_comment_action(report, action)

@spec put_comment_action(t(), comment_action()) :: t()

Sets comment_action.

summary(report)

@spec summary(t()) :: %{total: non_neg_integer(), by_severity: map(), by_kind: map()}

Counts findings by severity and by kind, plus the total — the numbers mix phoenix_kit.repair's human-readable summary line and --json output both render from.

to_json_map(report)

@spec to_json_map(t()) :: map()

Plain-data rendering for --json — every value is already a JSON scalar, list, or map except :versions.comment (:absent needs a string form) and :comment_action (a tagged tuple). Both are flattened here; everything else (finding.kind/finding.severity atoms included) is left for the caller's JSON encoder to stringify, matching how mix phoenix_kit.doctor/ release_check already emit atoms as plain output.