# `PhoenixKit.Install.Common`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.14.0/lib/phoenix_kit/install/common.ex#L1)

Common utilities shared between PhoenixKit installation and update tasks.

This module provides shared functionality for:
- Timestamp generation
- Version formatting
- Status checking
- Migration detection
- Registering PhoenixKit's Mix compilers in a host's `mix.exs`

# `check_installation_status`

Checks the current installation status for a given prefix.

Returns one of:
- `{:not_installed}` - the database is reachable and has no PhoenixKit install at the prefix
- `{:current_version, version}` - PhoenixKit is installed with given version
- `{:unknown_version}` - installed, but the version comment is missing or
  unreadable. Distinct from both of the above **because both of them route a
  caller somewhere that writes**: "not installed" to a fresh install, and a
  fabricated low version to the 1.7.x bridge, whose backfill invents uuids for
  still-NULL tracked columns and then deletes the rows that match no user.
  Callers must surface this and stop.
- `{:unreachable, reason}` - the database cannot be queried, so the install
  state is UNKNOWN — callers must not treat this as "not installed"

This list is the contract callers pattern-match against, so it is exhaustive
on purpose. `{:unknown_version}` was added after the other three, and the one
consumer whose clause set was not extended with it — `StatusReport.next_action/3`
— crashed `mix phoenix_kit.status` with a `FunctionClauseError` until it was.

## Parameters
- `prefix` - Database schema prefix (default: "public")

## Examples

    iex> PhoenixKit.Install.Common.check_installation_status("public")
    {:current_version, 3}

    iex> PhoenixKit.Install.Common.check_installation_status("auth")
    {:not_installed}

# `check_update_needed`

Checks if an update is needed from current to target version.

## Parameters
- `prefix` - Database schema prefix
- `force` - Force update even if already up to date

## Returns
- `{:up_to_date, current_version}` - Already up to date
- `{:update_needed, current_version, target_version}` - Update available
- `{:not_installed}` - PhoenixKit not installed
- `{:unknown_version}` - installed, version comment unreadable; neither up to
  date nor updatable, and answering either would send the caller somewhere
  that writes (see `check_installation_status/1`)
- `{:unreachable, reason}` - database cannot be queried (state unknown)

> #### No callers in this repo {: .info}
>
> Public API for host applications; nothing in `lib/` calls it today, so the
> clause set here is not exercised by the suite. Check it by hand when adding
> a state to `check_installation_status/1`.

# `current_version`

Gets current PhoenixKit version from migrations system.

# `describe_version_changes`

Describes what changed between versions.

## Parameters
- `from_version` - Starting version number
- `to_version` - Target version number

## Returns
String describing the changes between versions.

# `ensure_compilers_registered`

Ensures every atom in `compiler_names` is present in the host's `mix.exs`
`project/0` `:compilers` list — in ONE `Igniter.Project.MixProject.update/4`
call across every compiler PhoenixKit registers.

This must be a single call spanning all of them, not one call per compiler.
`Igniter.Code.List.prepend_new_to_list/2` only understands a literal list
AST. Registering against an absent `:compilers` key has to produce `atoms
++ Mix.compilers()` (a `++` call, not a literal list — `Mix.compilers()` is
a live call that can't be flattened at install time). A SECOND, separate
`Igniter.Project.MixProject.update/4` call touching the same key then lands
on that `++` node instead of a list, and `prepend_new_to_list/2` silently
fails into a `{:warning, ...}` that's easy to miss in the wall of `mix
phoenix_kit.update`/`mix phoenix_kit.install` output — so the second
compiler never actually gets registered even though the run reports
success. This is not hypothetical: it's exactly how a production host ended
up with `:phoenix_kit_css_sources` registered (added first) but
`:phoenix_kit_js_sources` silently missing (added second, in a separate
call) across many `phoenix_kit.update` runs — so PhoenixKit's own JS hook
fixes never reached the browser.

Also repairs a `:compilers` value an affected host is already stuck with in
that broken `[some_atom] ++ Mix.compilers()` shape: descends into the
literal list on the left of `++` and prepends there instead of bailing.

# `find_existing_phoenix_kit_migrations`

Finds existing PhoenixKit migrations in the project.

Returns a list of migration file paths.

# `generate_timestamp`

Generates timestamp in Ecto migration format.

## Examples

    iex> PhoenixKit.Install.Common.generate_timestamp()
    "20250908123045"

# `migrated_version`

Gets migrated version for given prefix.

## Parameters
- `prefix` - Database schema prefix

# `pad`

Pads a number with leading zero if less than 10.

## Examples

    iex> PhoenixKit.Install.Common.pad(5)
    "05"

    iex> PhoenixKit.Install.Common.pad(12)
    "12"

# `pad_version`

Pads version number for consistent naming.

## Examples

    iex> PhoenixKit.Install.Common.pad_version(1)
    "01"

    iex> PhoenixKit.Install.Common.pad_version(15)
    "15"

# `phoenix_kit_migration?`

Checks if a filename matches PhoenixKit migration pattern.

## Examples

    iex> PhoenixKit.Install.Common.phoenix_kit_migration?("20250908_add_phoenix_kit_tables.exs")
    true

    iex> PhoenixKit.Install.Common.phoenix_kit_migration?("20250908_create_users.exs")
    false

---

*Consult [api-reference.md](api-reference.md) for complete listing*
