For each (rover cell, fixed station) pair, sample SRTM along the great-circle path and add a per-link dB bonus proportional to the rover's elevation above the highest intermediate terrain. Rover spots on hilltops with clear sight to multiple stations now rise to the top. Also vendor Leaflet's layers control PNGs and copy them under priv/static/assets/css/images/ on build so /rover stops 404'ing on layers-2x.png.
209 lines
7.1 KiB
Elixir
209 lines
7.1 KiB
Elixir
defmodule Microwaveprop.MixProject do
|
|
use Mix.Project
|
|
|
|
def project do
|
|
[
|
|
app: :microwaveprop,
|
|
version: "0.1.0",
|
|
elixir: "~> 1.15",
|
|
elixirc_paths: elixirc_paths(Mix.env()),
|
|
start_permanent: Mix.env() == :prod,
|
|
aliases: aliases(),
|
|
deps: deps(),
|
|
compilers: [:phoenix_live_view] ++ Mix.compilers(),
|
|
listeners: [Phoenix.CodeReloader],
|
|
dialyzer: [
|
|
plt_add_apps: [:mix, :ex_unit],
|
|
plt_file: {:no_warn, "priv/plts/project.plt"},
|
|
flags: [
|
|
:error_handling,
|
|
:unknown,
|
|
:unmatched_returns,
|
|
:extra_return,
|
|
:missing_return
|
|
]
|
|
]
|
|
]
|
|
end
|
|
|
|
# Configuration for the OTP application.
|
|
#
|
|
# Type `mix help compile.app` for more information.
|
|
def application do
|
|
[
|
|
mod: {Microwaveprop.Application, []},
|
|
extra_applications: [:logger, :runtime_tools, :os_mon, :xmerl]
|
|
]
|
|
end
|
|
|
|
def cli do
|
|
[
|
|
preferred_envs: [precommit: :test]
|
|
]
|
|
end
|
|
|
|
# Specifies which paths to compile per environment.
|
|
defp elixirc_paths(:test), do: ["lib", "lib_ml", "test/support"]
|
|
defp elixirc_paths(:dev), do: ["lib", "lib_ml"]
|
|
defp elixirc_paths(_), do: ["lib"]
|
|
|
|
# Specifies your project dependencies.
|
|
#
|
|
# Type `mix help deps` for examples and options.
|
|
defp deps do
|
|
[
|
|
{:bcrypt_elixir, "~> 3.0"},
|
|
{:phoenix, "~> 1.8.3"},
|
|
{:phoenix_ecto, "~> 4.5"},
|
|
{:ecto_sql, "~> 3.13"},
|
|
{:postgrex, ">= 0.0.0"},
|
|
{:phoenix_html, "~> 4.1"},
|
|
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
|
{:phoenix_live_view, "~> 1.1.0"},
|
|
{:lazy_html, ">= 0.1.0", only: :test},
|
|
{:stream_data, "~> 1.0", only: :test},
|
|
{:live_stash, "~> 0.2"},
|
|
{:phoenix_live_dashboard, "~> 0.8.3"},
|
|
{:esbuild, "~> 0.10", runtime: Mix.env() == :dev},
|
|
{:tailwind, "~> 0.3", runtime: Mix.env() == :dev},
|
|
{:heroicons,
|
|
github: "tailwindlabs/heroicons", tag: "v2.2.0", sparse: "optimized", app: false, compile: false, depth: 1},
|
|
{:swoosh, "~> 1.16"},
|
|
{:gen_smtp, "~> 1.2"},
|
|
{:req, "~> 0.5"},
|
|
{:telemetry_metrics, "~> 1.0"},
|
|
{:telemetry_poller, "~> 1.0"},
|
|
{:prom_ex, "~> 1.11"},
|
|
{:jason, "~> 1.2"},
|
|
# Structured JSON log formatter plugged into Elixir 1.15+ :logger.
|
|
# OTP 28 / Elixir 1.19 ships `Logger.Formatter.new/1` but no built-in
|
|
# JSON encoder, so the hex package fills the gap. Prod flips to
|
|
# LOG_FORMAT=json; dev/test stay on the default text formatter.
|
|
{:logger_json, "~> 7.0"},
|
|
{:msgpax, "~> 2.4"},
|
|
{:bandit, "~> 1.5"},
|
|
{:styler, "~> 1.11", only: [:dev, :test], runtime: false},
|
|
{:oban, "~> 2.21"},
|
|
{:oban_met, "~> 1.0", path: "vendor/oban_met", override: true},
|
|
{:oban_pro, "~> 1.6", path: "vendor/oban_pro"},
|
|
{:oban_web, "~> 2.12", path: "vendor/oban_web", override: true},
|
|
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
|
|
{:nx, "~> 0.9", only: [:dev, :test]},
|
|
{:axon, "~> 0.7", only: [:dev, :test]},
|
|
{:exla, "~> 0.9", only: [:dev, :test]},
|
|
{:polaris, "~> 0.1", only: [:dev, :test]},
|
|
{:tidewave, "~> 0.5", only: :dev},
|
|
{:libcluster, "~> 3.4"},
|
|
{:ecto_psql_extras, "~> 0.8"},
|
|
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
|
|
{:live_table, "~> 0.4.1"},
|
|
# Transitive dep of live_table (via LiveTable.TableComponent's
|
|
# generated code pulls in SutraUI.DropdownMenu). Pinned here
|
|
# because live_table doesn't list it in its own deps.
|
|
{:sutra_ui, "~> 0.3.0"},
|
|
# Required as a compile-time build dependency for live_table; its mix
|
|
# tasks (lib/mix/tasks/live_table.gen.live.ex, live_table.install.ex)
|
|
# unconditionally `use Igniter.Mix.Task` so the dep has to exist in
|
|
# every env live_table is compiled in, not just dev/test. runtime: false
|
|
# keeps it out of the release bundle.
|
|
{:igniter, "~> 0.7", runtime: false}
|
|
]
|
|
end
|
|
|
|
# Aliases are shortcuts or tasks specific to the current project.
|
|
# For example, to install project dependencies and perform other setup tasks, run:
|
|
#
|
|
# $ mix setup
|
|
#
|
|
# See the documentation for `Mix` for more info on aliases.
|
|
defp aliases do
|
|
[
|
|
setup: ["deps.get", "ecto.setup", "assets.setup", "assets.build"],
|
|
"deps.get": ["deps.get", &apply_dep_patches/1],
|
|
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
|
|
"ecto.reset": ["ecto.drop", "ecto.setup"],
|
|
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
|
|
"assets.setup": ["tailwind.install --if-missing", "esbuild.install --if-missing"],
|
|
"assets.build": [
|
|
"compile",
|
|
"tailwind microwaveprop",
|
|
"esbuild microwaveprop",
|
|
©_leaflet_images/1
|
|
],
|
|
"assets.deploy": [
|
|
"tailwind microwaveprop --minify",
|
|
"esbuild microwaveprop --minify",
|
|
©_leaflet_images/1,
|
|
"phx.digest"
|
|
],
|
|
precommit: ["compile --warnings-as-errors", "deps.unlock --unused", "format", "test"]
|
|
]
|
|
end
|
|
|
|
# Mirrors `assets/vendor/leaflet/images/` into `priv/static/assets/css/images/`
|
|
# so Leaflet's CSS (which references `images/layers.png` etc. relative to the
|
|
# bundled stylesheet) can resolve those URLs in production. `priv/static/`
|
|
# is gitignored, so this needs to run on every build/deploy.
|
|
defp copy_leaflet_images(_args) do
|
|
src = "assets/vendor/leaflet/images"
|
|
dest = "priv/static/assets/css/images"
|
|
|
|
if File.dir?(src) do
|
|
File.mkdir_p!(dest)
|
|
File.cp_r!(src, dest)
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
# Applies every patch file in `priv/dep_patches/*.patch` to its corresponding
|
|
# dependency under `deps/`. Inlined here (instead of a `Mix.Task` module
|
|
# under `lib/mix/tasks/`) so it can run during `mix deps.get` itself —
|
|
# `lib/` isn't compiled yet at that point, so a proper task would not be
|
|
# resolvable. Idempotent: a sentinel file per patch prevents re-application.
|
|
@patches_dir "priv/dep_patches"
|
|
|
|
defp apply_dep_patches(_args) do
|
|
case File.ls(@patches_dir) do
|
|
{:ok, entries} ->
|
|
entries
|
|
|> Enum.filter(&String.ends_with?(&1, ".patch"))
|
|
|> Enum.sort()
|
|
|> Enum.each(&apply_single_patch/1)
|
|
|
|
{:error, :enoent} ->
|
|
:ok
|
|
end
|
|
end
|
|
|
|
defp apply_single_patch(filename) do
|
|
dep_name =
|
|
filename
|
|
|> Path.basename(".patch")
|
|
|> String.split("-")
|
|
|> Enum.drop(-1)
|
|
|> Enum.join("-")
|
|
|
|
dep_dir = Path.join("deps", dep_name)
|
|
sentinel = Path.join(dep_dir, ".#{filename}.applied")
|
|
|
|
cond do
|
|
not File.dir?(dep_dir) ->
|
|
Mix.shell().info("deps.patch: skipping #{filename} — deps/#{dep_name} not present")
|
|
|
|
File.exists?(sentinel) ->
|
|
Mix.shell().info("deps.patch: #{filename} already applied")
|
|
|
|
true ->
|
|
patch_path = Path.absname(Path.join(@patches_dir, filename))
|
|
{output, exit_code} = System.cmd("patch", ["-p1", "-i", patch_path], cd: dep_dir)
|
|
|
|
if exit_code == 0 do
|
|
Mix.shell().info("deps.patch: applied #{filename}")
|
|
File.touch!(sentinel)
|
|
else
|
|
Mix.raise("deps.patch: failed to apply #{filename}:\n#{output}")
|
|
end
|
|
end
|
|
end
|
|
end
|