diff --git a/lib/towerops/application.ex b/lib/towerops/application.ex index c3335111..669030d0 100644 --- a/lib/towerops/application.ex +++ b/lib/towerops/application.ex @@ -142,21 +142,33 @@ defmodule Towerops.Application do opts = [strategy: :one_for_one, name: Towerops.Supervisor] result = Supervisor.start_link(children, opts) - # Attach telemetry filter to suppress noisy health check logs - ToweropsWeb.TelemetryFilter.attach() - - # Run post-startup cleanup tasks (production only) - # This ensures all polling jobs use the latest worker code after deployment - _ = - Task.Supervisor.start_child(Towerops.TaskSupervisor, fn -> - # Wait for supervisor to fully initialize - Process.sleep(2000) - JobCleanupTask.run() - end) + if match?({:ok, _}, result), do: post_startup() result end + # Post-startup work runs only when the supervisor came up cleanly. The Task + # supervisor call is wrapped in try/catch so a transient noproc (the named + # supervisor may not be ready or may already be dying) cannot exit the boot + # process and mask the real error from Supervisor.start_link. + defp post_startup do + ToweropsWeb.TelemetryFilter.attach() + + _ = + try do + Task.Supervisor.start_child(Towerops.TaskSupervisor, fn -> + Process.sleep(2000) + JobCleanupTask.run() + end) + catch + :exit, reason -> + Logger.warning("Skipping JobCleanupTask: #{inspect(reason)}") + :error + end + + :ok + end + # Tell Phoenix to update the endpoint configuration # whenever the application is updated. @impl true diff --git a/lib/towerops/profiles/yaml_profiles.ex b/lib/towerops/profiles/yaml_profiles.ex index 1920d184..7c76dfb9 100644 --- a/lib/towerops/profiles/yaml_profiles.ex +++ b/lib/towerops/profiles/yaml_profiles.ex @@ -89,7 +89,8 @@ defmodule Towerops.Profiles.YamlProfiles do def init(_opts) do _ = :ets.new(@table, [:named_table, :set, :public, read_concurrency: true]) - if Mix.env() == :test do + # Mix isn't loaded in releases — use the runtime app env instead. + if Application.get_env(:towerops, :env) == :test do # Load synchronously so tests don't race with deferred loading. raw_profiles = load_all_profiles() mib_names = extract_mib_names_from_profiles(raw_profiles)