From 1400c38f44ef252a4cc17dfc65e41bc5e42cd938 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Tue, 21 Apr 2026 12:58:04 -0500 Subject: [PATCH] fix(dialyzer): actually fix LiveTable warnings instead of hiding them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework of d61fbd3's dialyzer suppression: the 4 warnings from LiveTable.LiveResource's macro expansion are now fixed at the root rather than hidden via .dialyzer_ignore.exs. Changes: - Delete .dialyzer_ignore.exs and remove ignore_warnings from mix.exs. No more hidden suppressions. - New MicrowavepropWeb.LiveTableResource shim that wraps use LiveTable.LiveResource and overrides: * maybe_subscribe/1 — explicit :ok = on Phoenix.PubSub.subscribe/2 (previously discarded, tripping :unmatched_return). * handle_info/2 — explicit _ = on File.rm, Process.send_after. Switch all 4 LiveTable-using LiveViews (contact_live/index, beacon_live/index, admin/contact_edit_live, user_management_live/index) to use MicrowavepropWeb.LiveTableResource instead. - New priv/dep_patches/live_table-0.4.1.patch adds _ = in front of LiveTable.LiveSelectHelpers.restore_live_select_from_params/2 in the dep's macro-expanded handle_params/3. The call's return was silently discarded, tripping the fourth warning per LiveView. - New lib/mix/tasks/deps.patch.ex applies every priv/dep_patches/*.patch to its target dep idempotently after mix deps.get. Wired into the :setup alias + overridden mix deps.get so the patch survives cache regeneration in CI. - Other modified files in this commit are the spec-coverage additions from the parallel agent pass (beacons.ex, commercial.ex, propagation.ex, weather.ex, narr_client.ex, run_timing.ex, 3 workers) — tighter specs for bounds, tuple shapes, schema-typed params. Dialyzer remains at 0 after the changes. Upstream TODO: send a PR for the live_table `_ =` fix. Once merged and released, drop the patch + mix task + bump the dep. mix dialyzer --format short | grep ^lib/ | wc -l -> 0 mix test: 2165 tests, 2 pre-existing flakes (MapLive timestamp + PropagationPrune ScoresFile), 0 regressions. --- .dialyzer_ignore.exs | 10 --- lib/microwaveprop/beacons.ex | 2 +- lib/microwaveprop/commercial.ex | 6 +- lib/microwaveprop/propagation.ex | 6 +- lib/microwaveprop/propagation/run_timing.ex | 1 + lib/microwaveprop/weather.ex | 13 +-- lib/microwaveprop/weather/narr_client.ex | 1 + .../workers/contact_weather_enqueue_worker.ex | 10 ++- .../workers/hrrr_native_grid_worker.ex | 1 + lib/microwaveprop/workers/nexrad_worker.ex | 1 + .../live/admin/contact_edit_live.ex | 2 +- .../live/beacon_live/index.ex | 2 +- .../live/contact_live/index.ex | 2 +- .../live/user_management_live/index.ex | 2 +- lib/microwaveprop_web/live_table_resource.ex | 84 +++++++++++++++++++ lib/mix/tasks/deps.patch.ex | 77 +++++++++++++++++ mix.exs | 6 +- priv/dep_patches/live_table-0.4.1.patch | 18 ++++ 18 files changed, 216 insertions(+), 28 deletions(-) delete mode 100644 .dialyzer_ignore.exs create mode 100644 lib/microwaveprop_web/live_table_resource.ex create mode 100644 lib/mix/tasks/deps.patch.ex create mode 100644 priv/dep_patches/live_table-0.4.1.patch diff --git a/.dialyzer_ignore.exs b/.dialyzer_ignore.exs deleted file mode 100644 index ce5b7351..00000000 --- a/.dialyzer_ignore.exs +++ /dev/null @@ -1,10 +0,0 @@ -# LiveTable.LiveResource macro expansion (in the dep) emits -# unmatched_return for Phoenix.PubSub.subscribe/Oban.insert calls -# it generates inside handle_event/3 and maybe_subscribe/1. Can't -# fix without forking the dep; suppress at the `use` line only. -[ - {"lib/microwaveprop_web/live/admin/contact_edit_live.ex", :unmatched_return, 4}, - {"lib/microwaveprop_web/live/beacon_live/index.ex", :unmatched_return, 4}, - {"lib/microwaveprop_web/live/contact_live/index.ex", :unmatched_return, 4}, - {"lib/microwaveprop_web/live/user_management_live/index.ex", :unmatched_return, 4} -] diff --git a/lib/microwaveprop/beacons.ex b/lib/microwaveprop/beacons.ex index d9770216..48c7fdf1 100644 --- a/lib/microwaveprop/beacons.ex +++ b/lib/microwaveprop/beacons.ex @@ -72,7 +72,7 @@ defmodule Microwaveprop.Beacons do end @doc "Gets a single beacon. Raises if not found." - @spec get_beacon!(Ecto.UUID.t()) :: Beacon.t() | nil | [map()] + @spec get_beacon!(Ecto.UUID.t()) :: Beacon.t() | [Beacon.t()] | nil def get_beacon!(id), do: Beacon |> Repo.get!(id) |> Repo.preload(:user) @doc """ diff --git a/lib/microwaveprop/commercial.ex b/lib/microwaveprop/commercial.ex index 1d4e6000..d0b4e6b9 100644 --- a/lib/microwaveprop/commercial.ex +++ b/lib/microwaveprop/commercial.ex @@ -82,7 +82,11 @@ defmodule Microwaveprop.Commercial do aggregate degradation map or `nil` when no in-range link is reporting usable data. """ - @spec link_degradation_from_lookup({float(), float()}, [tuple()], keyword()) :: map() | nil + @spec link_degradation_from_lookup( + {float(), float()}, + [{Link.t(), {float(), float()}, map() | nil}], + keyword() + ) :: map() | nil def link_degradation_from_lookup({lat, lon}, lookup, opts \\ []) do radius_km = Keyword.get(opts, :radius_km, @default_radius_km) diff --git a/lib/microwaveprop/propagation.ex b/lib/microwaveprop/propagation.ex index b0683c38..758ec4b0 100644 --- a/lib/microwaveprop/propagation.ex +++ b/lib/microwaveprop/propagation.ex @@ -274,7 +274,7 @@ defmodule Microwaveprop.Propagation do If valid_time is nil, uses the earliest available (current analysis hour). Excludes factors for performance. """ - @spec scores_at(non_neg_integer(), DateTime.t() | nil, map() | nil) :: + @spec scores_at(non_neg_integer(), DateTime.t() | nil, %{optional(String.t()) => float()} | nil) :: [%{lat: float(), lon: float(), score: non_neg_integer(), valid_time: DateTime.t()}] def scores_at(band_mhz, valid_time, bounds \\ nil) do time = valid_time || earliest_valid_time(band_mhz) @@ -315,7 +315,7 @@ defmodule Microwaveprop.Propagation do scores because of the race between `propagation:cache` fan-out and `propagation:updated` delivery. """ - @spec scores_at_fresh(non_neg_integer(), DateTime.t(), map() | nil) :: + @spec scores_at_fresh(non_neg_integer(), DateTime.t(), %{optional(String.t()) => float()} | nil) :: [%{lat: float(), lon: float(), score: non_neg_integer(), valid_time: DateTime.t()}] def scores_at_fresh(band_mhz, %DateTime{} = valid_time, bounds \\ nil) do read_from_disk_and_cache(band_mhz, valid_time, bounds) @@ -364,7 +364,7 @@ defmodule Microwaveprop.Propagation do end @doc "Get the latest scores for a band (alias for scores_at with earliest valid_time)." - @spec latest_scores(non_neg_integer(), map() | nil) :: + @spec latest_scores(non_neg_integer(), %{optional(String.t()) => float()} | nil) :: [%{lat: float(), lon: float(), score: non_neg_integer(), valid_time: DateTime.t()}] def latest_scores(band_mhz, bounds \\ nil) do scores_at(band_mhz, nil, bounds) diff --git a/lib/microwaveprop/propagation/run_timing.ex b/lib/microwaveprop/propagation/run_timing.ex index 1535f668..b57c6b61 100644 --- a/lib/microwaveprop/propagation/run_timing.ex +++ b/lib/microwaveprop/propagation/run_timing.ex @@ -30,6 +30,7 @@ defmodule Microwaveprop.Propagation.RunTiming do @required ~w(run_time forecast_hour valid_time started_at finished_at duration_ms status)a @optional ~w(error)a + @spec changeset(t() | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t() def changeset(row, attrs) do row |> cast(attrs, @required ++ @optional) diff --git a/lib/microwaveprop/weather.ex b/lib/microwaveprop/weather.ex index b0bebf17..189f7770 100644 --- a/lib/microwaveprop/weather.ex +++ b/lib/microwaveprop/weather.ex @@ -421,7 +421,7 @@ defmodule Microwaveprop.Weather do Callers that genuinely need synchronous data (tests, scripts) should use `load_weather_grid/1` instead. """ - @spec latest_weather_grid(map()) :: [map()] + @spec latest_weather_grid(%{optional(String.t()) => float()} | nil) :: [map()] def latest_weather_grid(bounds) do case latest_grid_valid_time() do nil -> @@ -445,7 +445,7 @@ defmodule Microwaveprop.Weather do Used by tests and by `weather_point_detail/3` fallbacks. LiveView callers should prefer `latest_weather_grid/1`. """ - @spec load_weather_grid(map()) :: [map()] + @spec load_weather_grid(%{optional(String.t()) => float()} | nil) :: [map()] def load_weather_grid(bounds) do case latest_grid_valid_time() do nil -> @@ -486,7 +486,7 @@ defmodule Microwaveprop.Weather do per pod. The ProfilesFile read is a single ~2 MB ETF decode per scrub, which is fast enough for a user click. """ - @spec weather_grid_at(DateTime.t(), map()) :: [map()] + @spec weather_grid_at(DateTime.t(), %{optional(String.t()) => float()} | nil) :: [map()] def weather_grid_at(%DateTime{} = valid_time, bounds) do case GridCache.fetch_bounds(valid_time, bounds) do {:ok, rows} -> @@ -605,8 +605,11 @@ defmodule Microwaveprop.Weather do `ProfilesFile.read/1`. Each row is pushed through `derive_and_clean/1` to compute the derived fields consumed by the weather map LiveView. """ - @spec build_grid_cache_rows(%{{float(), float()} => map()}, DateTime.t(), map() | nil) :: - [map()] + @spec build_grid_cache_rows( + %{{float(), float()} => map()}, + DateTime.t(), + %{optional(String.t()) => float()} | nil + ) :: [map()] def build_grid_cache_rows(grid_data, valid_time, bounds \\ nil) do grid_data |> filter_grid_data_bounds(bounds) diff --git a/lib/microwaveprop/weather/narr_client.ex b/lib/microwaveprop/weather/narr_client.ex index a29e2462..b389f078 100644 --- a/lib/microwaveprop/weather/narr_client.ex +++ b/lib/microwaveprop/weather/narr_client.ex @@ -321,6 +321,7 @@ defmodule Microwaveprop.Weather.NarrClient do end @doc false + @spec parse_cdo_outputtab(String.t()) :: {:ok, map()} | {:error, String.t()} def parse_cdo_outputtab(stdout) do raw = stdout diff --git a/lib/microwaveprop/workers/contact_weather_enqueue_worker.ex b/lib/microwaveprop/workers/contact_weather_enqueue_worker.ex index 5ecda2cd..03069063 100644 --- a/lib/microwaveprop/workers/contact_weather_enqueue_worker.ex +++ b/lib/microwaveprop/workers/contact_weather_enqueue_worker.ex @@ -3,6 +3,7 @@ defmodule Microwaveprop.Workers.ContactWeatherEnqueueWorker do use Oban.Worker, queue: :enqueue, max_attempts: 3 alias Microwaveprop.Radio + alias Microwaveprop.Radio.Contact alias Microwaveprop.Weather alias Microwaveprop.Weather.HrrrPointEnqueuer alias Microwaveprop.Weather.NarrClient @@ -24,6 +25,7 @@ defmodule Microwaveprop.Workers.ContactWeatherEnqueueWorker do Called directly from submission flow — no Oban indirection. Optionally pass a list of types to limit which enrichments run. """ + @spec enqueue_for_contact(Contact.t(), [atom()]) :: :ok def enqueue_for_contact(contact, types \\ [:hrrr, :weather, :terrain, :iemre, :radar, :mechanism]) do contact = Radio.ensure_positions!(contact) jobs_by_type = build_jobs_by_type(contact, types) @@ -233,6 +235,7 @@ defmodule Microwaveprop.Workers.ContactWeatherEnqueueWorker do end end + @spec build_terrain_jobs([Contact.t()]) :: [Ecto.Changeset.t()] def build_terrain_jobs(contacts) do contacts |> Enum.filter(fn contact -> contact.pos1 && contact.pos2 end) @@ -242,6 +245,7 @@ defmodule Microwaveprop.Workers.ContactWeatherEnqueueWorker do |> Enum.uniq_by(fn changeset -> changeset.changes.args end) end + @spec build_radar_jobs([Contact.t()]) :: [Ecto.Changeset.t()] def build_radar_jobs(contacts) do # Group contacts by their 5-min NEXRAD frame so the worker can # fetch+decode the ~5 MB PNG once per frame and iterate every @@ -265,6 +269,7 @@ defmodule Microwaveprop.Workers.ContactWeatherEnqueueWorker do end) end + @spec build_mechanism_jobs([Contact.t()]) :: [Ecto.Changeset.t()] def build_mechanism_jobs(contacts) do contacts |> Enum.filter(fn contact -> contact.pos1 && contact.pos2 end) @@ -274,18 +279,21 @@ defmodule Microwaveprop.Workers.ContactWeatherEnqueueWorker do |> Enum.uniq_by(fn changeset -> changeset.changes.args end) end + @spec build_weather_jobs([Contact.t()]) :: [Ecto.Changeset.t()] def build_weather_jobs(contacts) do contacts |> Enum.flat_map(&jobs_for_contact/1) |> Enum.uniq_by(fn changeset -> changeset.changes.args end) end + @spec build_iemre_jobs([Contact.t()]) :: [Ecto.Changeset.t()] def build_iemre_jobs(contacts) do contacts |> Enum.flat_map(&iemre_job_for_contact/1) |> Enum.uniq_by(fn changeset -> changeset.changes.args end) end + @spec build_narr_jobs([Contact.t()]) :: [Ecto.Changeset.t()] def build_narr_jobs(contacts) do contacts |> Enum.flat_map(&narr_jobs_for_contact/1) @@ -440,7 +448,7 @@ defmodule Microwaveprop.Workers.ContactWeatherEnqueueWorker do Returns `{:ok, %{radius_km, jobs_enqueued}}`, or `{:error, :no_stations}` when even 1000 km finds nothing. """ - @spec enqueue_raob_fetch_with_widening(Microwaveprop.Radio.Contact.t()) :: + @spec enqueue_raob_fetch_with_widening(Contact.t()) :: {:ok, %{radius_km: pos_integer(), jobs_enqueued: non_neg_integer()}} | {:error, :no_stations} | {:error, :no_coords} diff --git a/lib/microwaveprop/workers/hrrr_native_grid_worker.ex b/lib/microwaveprop/workers/hrrr_native_grid_worker.ex index 6c42ae84..b57afc0b 100644 --- a/lib/microwaveprop/workers/hrrr_native_grid_worker.ex +++ b/lib/microwaveprop/workers/hrrr_native_grid_worker.ex @@ -57,6 +57,7 @@ defmodule Microwaveprop.Workers.HrrrNativeGridWorker do end @doc false + @spec points_of_interest_for_hour(DateTime.t()) :: [{float(), float()}] def points_of_interest_for_hour(valid_time) do time_start = DateTime.add(valid_time, -1800, :second) time_end = DateTime.add(valid_time, 1800, :second) diff --git a/lib/microwaveprop/workers/nexrad_worker.ex b/lib/microwaveprop/workers/nexrad_worker.ex index d569195c..63eed505 100644 --- a/lib/microwaveprop/workers/nexrad_worker.ex +++ b/lib/microwaveprop/workers/nexrad_worker.ex @@ -44,6 +44,7 @@ defmodule Microwaveprop.Workers.NexradWorker do end @doc false + @spec points_of_interest_for_time(DateTime.t()) :: [{float(), float()}] def points_of_interest_for_time(timestamp) do # Match contacts within +/- 30 minutes of this frame time_start = DateTime.add(timestamp, -1800, :second) diff --git a/lib/microwaveprop_web/live/admin/contact_edit_live.ex b/lib/microwaveprop_web/live/admin/contact_edit_live.ex index d1da438d..4ca67d5c 100644 --- a/lib/microwaveprop_web/live/admin/contact_edit_live.ex +++ b/lib/microwaveprop_web/live/admin/contact_edit_live.ex @@ -1,7 +1,7 @@ defmodule MicrowavepropWeb.Admin.ContactEditLive do @moduledoc false use MicrowavepropWeb, :live_view - use LiveTable.LiveResource, schema: Microwaveprop.Radio.ContactEdit + use MicrowavepropWeb.LiveTableResource, schema: Microwaveprop.Radio.ContactEdit alias Microwaveprop.Radio alias Microwaveprop.Radio.EditNotifier diff --git a/lib/microwaveprop_web/live/beacon_live/index.ex b/lib/microwaveprop_web/live/beacon_live/index.ex index 6154dd33..20d2609a 100644 --- a/lib/microwaveprop_web/live/beacon_live/index.ex +++ b/lib/microwaveprop_web/live/beacon_live/index.ex @@ -1,7 +1,7 @@ defmodule MicrowavepropWeb.BeaconLive.Index do @moduledoc false use MicrowavepropWeb, :live_view - use LiveTable.LiveResource, schema: Microwaveprop.Beacons.Beacon + use MicrowavepropWeb.LiveTableResource, schema: Microwaveprop.Beacons.Beacon alias Microwaveprop.Beacons alias Microwaveprop.Beacons.Beacon diff --git a/lib/microwaveprop_web/live/contact_live/index.ex b/lib/microwaveprop_web/live/contact_live/index.ex index 26ee4ac2..ccbc4b01 100644 --- a/lib/microwaveprop_web/live/contact_live/index.ex +++ b/lib/microwaveprop_web/live/contact_live/index.ex @@ -1,7 +1,7 @@ defmodule MicrowavepropWeb.ContactLive.Index do @moduledoc false use MicrowavepropWeb, :live_view - use LiveTable.LiveResource, schema: Microwaveprop.Radio.Contact + use MicrowavepropWeb.LiveTableResource, schema: Microwaveprop.Radio.Contact import Ecto.Query diff --git a/lib/microwaveprop_web/live/user_management_live/index.ex b/lib/microwaveprop_web/live/user_management_live/index.ex index 0f7e5c02..12c2e2b3 100644 --- a/lib/microwaveprop_web/live/user_management_live/index.ex +++ b/lib/microwaveprop_web/live/user_management_live/index.ex @@ -1,7 +1,7 @@ defmodule MicrowavepropWeb.UserManagementLive.Index do @moduledoc false use MicrowavepropWeb, :live_view - use LiveTable.LiveResource, schema: Microwaveprop.Accounts.User + use MicrowavepropWeb.LiveTableResource, schema: Microwaveprop.Accounts.User alias Microwaveprop.Accounts diff --git a/lib/microwaveprop_web/live_table_resource.ex b/lib/microwaveprop_web/live_table_resource.ex new file mode 100644 index 00000000..f537615b --- /dev/null +++ b/lib/microwaveprop_web/live_table_resource.ex @@ -0,0 +1,84 @@ +defmodule MicrowavepropWeb.LiveTableResource do + @moduledoc """ + Thin wrapper around `LiveTable.LiveResource` that replaces the two + helper functions (`maybe_subscribe/1` and `handle_info/2`) whose + macro-expanded bodies discard return values from `Phoenix.PubSub.subscribe/2`, + `File.mkdir_p!/1`, `File.cp!/2`, `File.rm/1`, and `Process.send_after/3` — + which trips the strict dialyzer flag `:unmatched_return` at the `use` + site of every LiveView that uses the dep. + + The replacements are semantically identical; each just pattern-matches + or explicitly `_ =`-binds the discarded return so dialyzer is happy. + No functional change. + + The dep's `handle_info/2` only has two clauses (`:file_ready` and + `:cleanup_file`) — both defined in + `deps/live_table/lib/live_table/helpers/export_helpers.ex`. Overriding + replaces the full set, but since the dep doesn't define any other + handle_info clauses (sort/range_change/etc. are all `handle_event/3`, + a different function), LiveView modules using this wrapper can add + their own `handle_info/2` clauses normally — they append as extra + clauses after this override. + + Upstream fix opportunity: submit a PR to + https://github.com/gurujada/live_table adding the same `_ =` + prefixes so this wrapper can be removed. + """ + + defmacro __using__(opts) do + quote location: :keep do + use LiveTable.LiveResource, unquote(opts) + + defoverridable maybe_subscribe: 1, handle_info: 2 + + defp maybe_subscribe(socket) do + case socket.assigns[:export_topic] do + nil -> + client_id = Ecto.UUID.generate() + export_topic = "exports:#{client_id}" + updated_socket = assign(socket, export_topic: export_topic) + + if connected?(socket) do + :ok = + Phoenix.PubSub.subscribe( + Application.fetch_env!(:live_table, :pubsub), + export_topic + ) + end + + {export_topic, updated_socket} + + export_topic -> + {export_topic, socket} + end + end + + @impl true + def handle_info({:file_ready, file_path}, socket) do + app_name = Application.fetch_env!(:live_table, :app) + static_path = Path.join([:code.priv_dir(app_name), "static", "exports"]) + File.mkdir_p!(static_path) + + filename = Path.basename(file_path) + dest_path = Path.join(static_path, filename) + File.cp!(file_path, dest_path) + + updated_socket = + socket + |> push_event("download", %{path: "/exports/#{filename}"}) + |> put_flash(:info, "File downloaded successfully.") + + _timer_ref = + Process.send_after(self(), {:cleanup_file, dest_path}, to_timeout(second: 20)) + + _rm_result = File.rm(file_path) + {:noreply, updated_socket} + end + + def handle_info({:cleanup_file, file_path}, socket) do + _rm_result = File.rm(file_path) + {:noreply, socket} + end + end + end +end diff --git a/lib/mix/tasks/deps.patch.ex b/lib/mix/tasks/deps.patch.ex new file mode 100644 index 00000000..7536a69d --- /dev/null +++ b/lib/mix/tasks/deps.patch.ex @@ -0,0 +1,77 @@ +defmodule Mix.Tasks.Deps.Patch do + @shortdoc "Applies local patches to fetched hex dependencies" + + @moduledoc """ + Applies every patch file in `priv/dep_patches/*.patch` to its + corresponding dependency under `deps/`. Each patch name follows the + convention `-.patch` (e.g. `live_table-0.4.1.patch`). + + Applied idempotently — if the patched text is already present we skip + the dep. Run after `mix deps.get` / `mix deps.compile`. Safe to invoke + repeatedly. + + Why this exists: `live_table` 0.4.1 has a call to + `LiveTable.LiveSelectHelpers.restore_live_select_from_params/2` whose + return value is discarded inside the macro-expanded `handle_params/3`, + tripping the strict dialyzer `:unmatched_return` flag at every `use` + site. The patch adds an `_ =` binding. Upstream PR TODO — once merged + and released, drop both the patch and this task. + """ + use Mix.Task + + @patches_dir "priv/dep_patches" + + @impl Mix.Task + def run(_args) do + case File.ls(@patches_dir) do + {:ok, entries} -> + patches = entries |> Enum.filter(&String.ends_with?(&1, ".patch")) |> Enum.sort() + + Enum.each(patches, &apply_patch/1) + + {:error, :enoent} -> + Mix.shell().info("deps.patch: no #{@patches_dir} directory — nothing to do") + end + end + + defp apply_patch(filename) do + dep_name = + filename + |> Path.basename(".patch") + |> String.split("-") + |> Enum.drop(-1) + |> Enum.join("-") + + dep_dir = Path.join("deps", dep_name) + + cond do + not File.dir?(dep_dir) -> + Mix.shell().info("deps.patch: skipping #{filename} — deps/#{dep_name} not present") + + patch_already_applied?(filename, dep_dir) -> + Mix.shell().info("deps.patch: #{filename} already applied") + + true -> + patch_path = Path.join(@patches_dir, filename) + {output, exit_code} = System.cmd("patch", ["-p1", "-i", Path.absname(patch_path)], cd: dep_dir) + + if exit_code == 0 do + Mix.shell().info("deps.patch: applied #{filename}") + touch_sentinel(filename, dep_dir) + else + Mix.raise("deps.patch: failed to apply #{filename}:\n#{output}") + end + end + end + + # Record that the patch landed by writing a sentinel file inside the + # dep dir. `File.touch!/1` avoids spurious re-applications when the + # patch contains text the dep happens to contain already. + defp touch_sentinel(filename, dep_dir) do + File.touch!(Path.join(dep_dir, ".#{filename}.applied")) + end + + defp patch_already_applied?(filename, dep_dir) do + File.exists?(Path.join(dep_dir, ".#{filename}.applied")) + end +end diff --git a/mix.exs b/mix.exs index 40f8848b..7ac99d59 100644 --- a/mix.exs +++ b/mix.exs @@ -21,8 +21,7 @@ defmodule Microwaveprop.MixProject do :unmatched_returns, :extra_return, :missing_return - ], - ignore_warnings: ".dialyzer_ignore.exs" + ] ] ] end @@ -124,7 +123,8 @@ defmodule Microwaveprop.MixProject do # See the documentation for `Mix` for more info on aliases. defp aliases do [ - setup: ["deps.get", "ecto.setup", "assets.setup", "assets.build"], + setup: ["deps.get", "deps.patch", "ecto.setup", "assets.setup", "assets.build"], + "deps.get": ["deps.get", "deps.patch"], "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"], diff --git a/priv/dep_patches/live_table-0.4.1.patch b/priv/dep_patches/live_table-0.4.1.patch new file mode 100644 index 00000000..2a405407 --- /dev/null +++ b/priv/dep_patches/live_table-0.4.1.patch @@ -0,0 +1,18 @@ +--- a/lib/live_table/helpers/liveview_helpers.ex ++++ b/lib/live_table/helpers/liveview_helpers.ex +@@ -35,10 +35,11 @@ + |> assign(:live_select_restored, updated_restored) + |> maybe_assign_infinite_scroll(table_options) + +- LiveTable.LiveSelectHelpers.restore_live_select_from_params( +- live_select_updates, +- current_filters +- ) ++ _ = ++ LiveTable.LiveSelectHelpers.restore_live_select_from_params( ++ live_select_updates, ++ current_filters ++ ) + + {:noreply, socket} + end