Adds a self-contained 'unused' Mix compiler task that finds public
functions in this project no caller in the project ever invokes:
- MixUnused.Analyzer: enumerates public functions across the project's
compiled BEAM files (filtered by app), walks the abstract code in
each BEAM via :beam_lib to collect every remote {Module, fun, arity}
call observed at compile time, and subtracts the call set from the
def set. Exposes :exclude (modules or MFA triples), :compile_path,
and :app options. Behaviour callbacks (@impl true and any listed
in behaviour_info(:callbacks)) and well-known entry points
(start_link, child_spec, mount, render, GenServer callbacks,
__info__, module_info, etc.) are excluded automatically.
- Mix.Tasks.Compile.Unused: a Mix.Task.Compiler that compiles the
project then asks the analyzer for unused defs. Severity is
configurable via the :unused project key; per-file exclusion of
test/ paths is on by default.
Inspired by https://github.com/hauleth/mix_unused — uses BEAM
introspection rather than a compilation tracer to avoid the
chicken-and-egg problem of recompiling the tracer module itself
during analysis.
Adds tests for the PromEx supervision module and custom plugin (both
were 0–25% covered), expands the HealthCheck plug to exercise the
readiness success path through the live ShutdownHandler, adds public-API
roundtrip tests for PacketReplay's via_tuple wrappers, and covers
several private-helper fallback branches in Aprsme.Packet
(ParseError data_extended, MicE map symbol defaults, struct
data_extended, weather binary, has_position via legacy lat/lon, PHG /
altitude error parsing).
Wire up prom_ex with built-in Application, Beam, Phoenix, PhoenixLiveView,
and Ecto plugins, plus a custom plugin that exposes the existing
app-specific telemetry (packet pipeline, spatial PubSub, repo pool,
postgres stats, insert optimizer) as Prometheus metrics.
Mount PromEx.Plug at /metrics — internal-only, intended to be scraped
by the cluster's external Prometheus through the kube-apiserver pod
proxy. K8s pod template now carries prometheus.io/{scrape,port,path}
annotations so the existing kubernetes-pods scrape job picks it up
automatically.
PromEx is disabled in the test environment.
Also harden the packet cleanup and partition manager tests with
Repo.delete_all setup hooks so they aren't poisoned by residual rows
committed outside a sandbox transaction in earlier runs (which were
inflating delete counts and triggering ATTACH PARTITION check_violation
errors against packets_default).
Coverage:
- Adds focused tests across 13 files covering previously-uncovered
branches: Packet changeset normalisation (course, wind_direction,
struct data_extended); Packets store_packet edge cases (nested
position shapes, ssid handling, string-keyed raw_packet);
DeviceIdentification HTTP success/404 via Req plug stub; ResendAdapter
catch-all body, deliver_many, nil/binary formatters; ApiDocsLive
packets with missing fields; AprsSymbol normalize helpers;
BadPacketsLive refresh and postgres_notify; MobileChannel handle_info
catch-all and postgres_packet path; PacketProcessor existing-marker
movement detection; PacketReplay sanitize_value type clauses;
InsertOptimizer GenServer lifecycle and negative-duration metric;
PageController shutdown-handler integration; UrlParams delegated
helpers.
- mix.exs: configure test_coverage with summary threshold of 87 and
ignore_modules for test fixtures and macro-only template modules.
Bug fix:
- MapLive.Index handle_info: add a clause for the raw {:new_deployment,
_} tuple. Phoenix.PubSub.broadcast delivers the raw payload to plain
subscribers, but the existing handler only matched the %Broadcast{}
fast-lane wrapper. DeploymentNotifier broadcasts via the raw path,
which crashed the LiveView under test concurrency.
Submodule:
- Update .gitmodules vendor/aprs URL from
https://github.com/aprsme/aprs.git to
ssh://git@codeberg.org/gmcintire/aprs.git.
- Delete config/appsignal.exs (was never wired into mix.exs or any
other config file)
- Migration: fix decrement_packet_sequence trigger to handle the case
where currval() hasn't been called in the current DB session, matching
the same exception handling already present in get_packet_count()
- Fix get_historical_packet_count test: invalid non-list bounds are
silently ignored (no error), so assert is_integer not count == 0
- Add Repo.delete_all(Packet) setup blocks in PacketsOldestTest and
PacketConsumerTest to clear any committed DB state before tests that
depend on an empty packets table
- maxSurge: 1 (explicit integer, avoid percentage rounding)
- minReadySeconds: 30 (was 10) — new pod must be stable longer before old is killed
- progressDeadlineSeconds: 600 — detect stuck rollouts
- readinessProbe failureThreshold: 3 (was 2) — more tolerance after old pod is gone
- Add PodDisruptionBudget minAvailable: 1 — prevents voluntary disruptions from dropping below 1 pod
Forgejo runner with default Docker emits OCI image-index manifests for
multi-arch + attestation builds. argocd-image-updater cannot follow the
index → manifest chain to read the Created timestamp, so it never sees
new tags. Pinning to a single linux/amd64 build with no provenance/sbom
produces a plain Docker v2 manifest that image-updater can read.
Forgejo Actions executes both .github/workflows/ and .forgejo/workflows/.
deploy.yml was a duplicate of .forgejo/workflows/build.yaml that used
docker/build-push-action@v5, which produces OCI image-index manifests
that argocd-image-updater cannot read. Removing it leaves only the
classic-docker-build workflow.
elixir.yaml, claude.yml, claude-code-review.yml, dependency-updates.yml
are kept (no forgejo equivalents).
The previous commit referenced an image tag that was never pushed to the registry,
breaking deploys. Pin to the last working tag until CI produces a new build.