Current section
Files
Jump to
Current section
Files
mailglass_admin
mix.exs
mix.exs
defmodule MailglassAdmin.MixProject do
use Mix.Project
@version "2.4.1"
@source_url "https://github.com/szTheory/mailglass"
@description "Mountable LiveView dashboard for mailglass — dev preview + admin"
def project do
[
app: :mailglass_admin,
version: @version,
elixir: "~> 1.18",
elixirc_paths: elixirc_paths(Mix.env()),
elixirc_options: elixirc_options(),
compilers: [:boundary | Mix.compilers()],
start_permanent: Mix.env() == :prod,
deps: deps(),
aliases: aliases(),
dialyzer: dialyzer(),
name: "MailglassAdmin",
description: @description,
source_url: @source_url,
homepage_url: @source_url,
package: package(),
docs: docs()
]
end
def application do
[extra_applications: [:logger]]
end
def cli do
[
preferred_envs: [
ci: :test,
"ci.fast": :test,
"mailglass_admin.preview.capture": :test,
"verify.preview": :test,
"verify.phase_05": :test,
"verify.support_contract.admin": :test
]
]
end
defp dialyzer do
[
# D-08-01: :no_opaque + :no_match kill the Elixir 1.18 opaque-type
# cascade (elixir-lang/elixir#14837). :error_handling, :missing_return,
# :underspecs improve coverage; revisit removing :underspecs after the
# <=15-entry baseline is hit (D-08-07).
flags: [:error_handling, :missing_return, :no_opaque, :no_match, :underspecs],
# D-08-03: ignore_file_strict pins to {file, short_description} tuples
# (stable across line-number drift). D-08-04: list_unused_filters fails
# CI loudly when a future fix invalidates an existing ignore (prevents
# silent drift).
ignore_file_strict: ".dialyzer_ignore.exs",
list_unused_filters: true,
# Add :credo and :mix to PLT so dev-only modules don't produce
# callback_info_missing + unknown_function warnings. credo is a :dev/:test
# dep; without :credo in the PLT, the 13 custom check files each generate
# ~11 dialyzer warnings. Mix.Task behaviour is similarly dev-only.
plt_add_apps: [:credo, :mix, :ex_unit]
]
end
# "dev/" holds internal CI-only preview-capture tooling (Chromium screenshot
# driver + mix mailglass_admin.preview.capture). Compiled in :dev and :test for
# CI/local use, but kept out of "lib" so it never ships in the Hex package
# (mix.exs :package :files lists "lib", not "dev").
defp elixirc_paths(:dev), do: ["lib", "dev"]
# RATCHET-01 / CONTEXT D-06 mechanism 2 (A1 fallback — Pitfall 3): the
# canonical persona cohort spec lives in the demo app, but a `path:` dep on
# the whole `mailglass_demo` app is structurally impossible — the demo
# depends on `mailglass_admin`, so a back-dep is a circular path dep ("another
# project with the same name was already defined"). Instead we compile the
# single canonical spec directory directly into the admin TEST build.
# `reference/persona_spec/personas.ex` is a pure module — it references only
# core schemas (`Mailglass.Outbound.Delivery`, `Mailglass.Events.Event`), no
# demo-app or Phoenix deps — so it compiles cleanly here. This keeps the spec
# a single source of truth (admin test-support reads
# `MailglassDemo.Personas.spec/0`) without crossing into prod or the
# `--no-optional-deps` lane (it is `:test`-only, just like `test/support`).
@persona_spec_dir Path.expand("../reference/persona_spec", __DIR__)
defp elixirc_paths(:test), do: ["lib", "test/support", "dev", @persona_spec_dir]
defp elixirc_paths(_), do: ["lib"]
# CONTEXT D-24: phoenix_live_reload is dev-only optional; declare here so
# bare references compile cleanly on the no-optional-deps CI lane.
defp elixirc_options do
[no_warn_undefined: [Phoenix.LiveReloader]]
end
defp deps do
[
# Local-dev: path dep so changes in ../lib/mailglass/ are picked up
# immediately. Published Hex tarball: pinned version match (linked
# versions per CONTEXT D-02 / DIST-01 / PREV-01).
mailglass_dep(),
# Optional sibling: the admin LiveView reads inbound rows through the
# `MailglassAdmin.OptionalDeps.MailglassInbound` runtime gateway. The dep
# is FLOATING (`~> 0.2`, never `==`) and `optional: true` so the
# `mix compile --no-optional-deps --warnings-as-errors` lane compiles with
# inbound stripped (CONTEXT D-48-01, Pitfall 4). It is INTENTIONALLY OUT of
# the release-please PINS array — a `==` cross-line pin into a 0.2.x package
# is unsatisfiable; the gateway, not a version pin, is the integration seam.
mailglass_inbound_dep(),
{:phoenix, "~> 1.8"},
{:phoenix_live_view, "~> 1.1"},
{:phoenix_html, "~> 4.1"},
{:plug, "~> 1.18"},
{:nimble_options, "~> 1.1"},
# Build tooling (CONTEXT D-18). No :esbuild at v0.1 (pure LiveView, no custom JS).
{:tailwind, "~> 0.4", only: [:dev, :test], runtime: false},
# Optional dev dep (CONTEXT D-24). Adopter-owned LiveReload subscription.
# `:only [:dev, :test]` so preview_live_test.exs can exercise the
# LiveReload subscribe + broadcast path; the dep remains `optional: true`
# so adopters can omit it entirely in prod-admin (v0.5) configurations.
{:phoenix_live_reload, "~> 1.6", optional: true, only: [:dev, :test]},
{:plug_cowboy, "~> 2.7", only: :test},
{:boundary, "~> 0.10", runtime: false},
# floki + jason: unrestricted :only scope because the mailglass core
# path dep uses them at runtime. Mix rejects divergent :only options
# on shared transitive deps (Rule 3 blocker resolution).
{:floki, "~> 0.38"},
{:jason, "~> 1.4"},
# Dev/test
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:ex_doc, "~> 0.40", only: :dev, runtime: false},
# Phoenix.LiveViewTest 1.1+ requires lazy_html for DOM traversal
# (replaces the previous floki-based implementation).
{:lazy_html, ">= 0.1.0", only: :test}
]
end
# CONTEXT D-02 linked-versions switch (the ONE pattern with no analog in
# mailglass core's mix.exs): local-dev uses a path dep so the sibling
# packages evolve together; publishing to Hex uses a pessimistic `~>` constraint
# (v1.15 Phase 125, LD-2). Admin is in the linked-versions group
# [mailglass, mailglass_admin] — the Release Please plugin release-time-locks
# admin's minor to core, so `~> 2.0` is safe: admin never resolves against a
# core minor it was not shipped with. A new core minor requires a linked admin
# release, which updates this line.
defp mailglass_dep do
if System.get_env("MIX_PUBLISH") == "true" do
{:mailglass, "~> 2.0"}
else
{:mailglass, path: "..", override: true}
end
end
# CONTEXT D-48-01: the optional inbound sibling. Mirrors `mailglass_dep/0`'s
# MIX_PUBLISH branch STRUCTURE (path dep for local-dev, version constraint when
# publishing) but is FLOATING (`~> 2.0`, NEVER `== X.Y.Z`) and `optional: true`.
#
# Why floating, not pinned: `mailglass_inbound` tracks its own version line and
# is NOT part of the release-please linked-versions group. The v2.0 milestone
# advanced inbound to its own 2.0.0 (breaking schema-isolation contract), so the
# floating constraint is `~> 2.0` — it must resolve against the inbound MAJOR
# that ships alongside this admin release. A `==` pin (the shape release-please's
# sed step writes for the linked siblings) could write an unsatisfiable
# cross-line version, so this dep is deliberately ABSENT from the release-please
# PINS array in `.github/workflows/release-please.yml`. Bump the floating line by
# hand when inbound's major advances past core's (0.2 -> 1.x -> 2.0).
#
# The admin reads inbound rows exclusively through the
# `MailglassAdmin.OptionalDeps.MailglassInbound` runtime gateway
# (`Code.ensure_loaded?(MailglassInbound)` + `apply/3`), so `optional: true`
# keeps the `--no-optional-deps` compile lane green with inbound stripped.
defp mailglass_inbound_dep do
if System.get_env("MIX_PUBLISH") == "true" do
{:mailglass_inbound, "~> 2.0", optional: true}
else
{:mailglass_inbound, path: "../mailglass_inbound", optional: true}
end
end
# REL-03: Semantic names are canonical. The `verify.phase_05` key below is a
# deprecated one-cycle pass-through that delegates to the semantic alias.
# Remove it in the next release cycle.
#
# Phase 5 verification gate. Intentionally RED at Plan 02 completion:
# - step 2 (test --warnings-as-errors) fails because Plans 03-06 tests are RED
# - step 3 (mailglass_admin.assets.build) fails because Plan 05 ships that task
# Step 4 is the PREV-06 / CONTEXT D-04 merge gate — bundle drift CI check.
defp aliases do
[
# Sibling-local ci verb (uniform "is this green?" across packages). The
# root `mix ci` already fans out; these serve the inner-loop-in-a-subdir
# case for a contributor working inside mailglass_admin/.
"ci.fast": [
"format --check-formatted",
"compile --no-optional-deps --warnings-as-errors",
"credo --strict"
],
ci: [
"ci.fast",
"verify.support_contract.admin"
],
# Semantic alias (REL-03)
"verify.preview": [
"compile --no-optional-deps --warnings-as-errors",
"test --warnings-as-errors --exclude flaky",
"mailglass_admin.assets.build",
"cmd git diff --exit-code priv/static/"
],
"verify.support_contract.admin": [
# Preview consumes the same renderer contract as direct, sync, and
# async delivery. Keep that parity in the existing required admin lane.
"test test/mailglass_admin/post_installer_smoke_test.exs test/mailglass_admin/operator_live_test.exs test/mailglass_admin/operator_trust_doc_test.exs test/mailglass_admin/stability_contract_test.exs test/mailglass_admin/router_test.exs test/mailglass_admin/auth_test.exs test/mailglass_admin/token_parity_test.exs test/mailglass_admin/ratchet_baseline_test.exs test/mailglass_admin/preview_live_test.exs --warnings-as-errors"
],
# Deprecated pass-through (REL-03, one cycle) — use verify.preview instead
"verify.phase_05": ["verify.preview"]
]
end
# CONTEXT D-04: strict files whitelist. `assets/` source is EXCLUDED from
# the Hex tarball (vendored daisyUI + Tailwind input); `priv/static/` is
# INCLUDED (compiled CSS, fonts, logo). LiveDashboard / Oban Web precedent.
defp package do
[
name: "mailglass_admin",
licenses: ["MIT"],
description: @description,
source_ref_pattern: "mailglass-sibling-group-v%{version}",
links: %{
"GitHub" => @source_url,
"HexDocs" => "https://hexdocs.pm/mailglass_admin"
},
files: ~w(lib priv/static docs .formatter.exs mix.exs README* CHANGELOG* LICENSE*)
]
end
defp docs do
[
main: "MailglassAdmin",
source_url: @source_url,
source_ref: "v" <> @version,
logo: "../brandbook/assets/logo-mark.svg",
favicon: "../brandbook/assets/favicon.svg",
extras: [
"README.md",
"docs/design-system.md",
"docs/operator-trust.md",
"docs/api_stability.md",
"docs/compatibility-and-deprecations.md"
],
groups_for_extras: [
Overview: ["README.md"],
Design: ["docs/design-system.md"],
Contract: [
"docs/operator-trust.md",
"docs/api_stability.md",
"docs/compatibility-and-deprecations.md"
]
],
groups_for_modules: [
Stable: [MailglassAdmin, MailglassAdmin.Router, MailglassAdmin.Auth],
Internal: [MailglassAdmin.Operator.Mount]
]
]
end
end