fix: use runtime env config in YamlProfiles init (broken in releases)

Mix isn't loaded in mix releases, so Mix.env() raised
UndefinedFunctionError inside YamlProfiles.init/1, crashing the
GenServer at boot. That made Supervisor.start_link return {:error, ...}
in production, which the application-start callback then masked by
exiting on the unrelated Task.Supervisor.start_child noproc — leaving
the pod in CrashLoopBackoff with no clear log of the real failure.

Switch to Application.get_env(:towerops, :env), which is set in all
three environments (dev/test/runtime).

Also harden the post-startup callback so a transient TaskSupervisor
noproc cannot mask a real Supervisor.start_link error in the future.
This commit is contained in:
Graham McIntire 2026-05-01 16:40:10 -05:00
parent a99c327446
commit 8729e36aff
2 changed files with 25 additions and 12 deletions

View file

@ -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

View file

@ -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)