Skip to content

ci(dialyzer): cache PLT and split build/analyze steps#243

Merged
davydog187 merged 4 commits into
mainfrom
ci/dialyzer-plt-cache
May 27, 2026
Merged

ci(dialyzer): cache PLT and split build/analyze steps#243
davydog187 merged 4 commits into
mainfrom
ci/dialyzer-plt-cache

Conversation

@davydog187
Copy link
Copy Markdown
Contributor

Cache the Dialyzer PLT in CI to cut job time from ~3m30s to <60s

Plan: .agents/plans/A38-dialyzer-plt-cache.md

Goal

Cache the Dialyzer PLT files in GitHub Actions so the dialyzer job runs in
under 60 seconds on cache hits instead of ~3m30s every run.

Root cause

The dialyzer job had no PLT cache and no stable PLT path. Every run
rebuilt the entire OTP/Elixir/ex_unit PLT from scratch (~2m45s), then
ran analysis on top of that (~15s). The PLT build cost was paid even
when nothing changed.

Success criteria

  • mix format --check-formatted passes
  • mix compile --warnings-as-errors passes
  • mix test passes — 55 doctests, 51 properties, 1771 tests, 0 failures
  • CI YAML is valid (manually reviewed)
  • On first CI run (cache miss): dialyzer job completes successfully (~3 min, same as today)
  • On second CI run (cache hit): mix dialyzer step < 60s

Changes

 .github/workflows/ci.yml | 12 ++++++++++++
 .gitignore               |  3 +++
 mix.exs                  |  4 +++-
 3 files changed, 18 insertions(+), 1 deletion(-)

mix.exs — pin plt_core_path/plt_local_path to priv/plts/ so the
PLT files land in a deterministic, cache-able directory. Also drop :mix
from plt_add_appslib/ has no runtime references to Mix.* (only
tasks/ does, and that path is excluded from the production
elixirc_paths/1).

.gitignore — exclude /priv/plts/ so PLT build artefacts are never committed.

.github/workflows/ci.yml — in the dialyzer job:

  • Add Restore PLT cache step (actions/cache@v4) immediately after
    install-deps. Cache key includes OS, Elixir version, OTP version, and
    mix.lock hash. The restore-keys fallback means a mix.lock bump
    triggers an incremental PLT update instead of a full rebuild.
  • Split the single mix dialyzer step into Build PLT (mix dialyzer --plt)
    and Run dialyzer (mix dialyzer) so CI logs show build vs. analysis
    time separately.

Verification

mix format --check-formatted    # ✓ clean
mix compile --warnings-as-errors # ✓ no warnings
mix test                         # ✓ 1771 tests, 0 failures, 30 skipped

Out of scope (intentional)

  • Changing what dialyzer checks (no new ignores, no flag changes)
  • Moving dialyzer off the merge-gating path
  • Enabling dialyzer on the OTP 29 / Elixir 1.20 matrix row
  • Any change to mix test or other CI jobs

Pin plt_core_path and plt_local_path to priv/plts/ in mix.exs so the
PLT lands in a stable, cache-able directory. Add a Restore PLT cache
step in the dialyzer job (actions/cache@v4, keyed on OS/Elixir/OTP/mix.lock)
so the ~3-minute PLT build is paid only on the first run or after a
dependency change. Split the single 'mix dialyzer' call into a separate
'Build PLT' step and 'Run dialyzer' step so CI logs show build vs.
analysis time independently.

Also drop :mix from plt_add_apps — lib/ has no runtime references to
Mix.*, so the extra PLT coverage is dead weight.

Expected result: dialyzer job drops from ~3m30s to ~45-60s on cache hit.
tasks/ uses Mix.raise/1, Mix.shell/0, and Mix.Task.run/1 — all compiled
under the :dev env that dialyzer analyses. Dropping :mix caused 18
unknown_function errors. Put it back.
@davydog187 davydog187 merged commit 4d85ee0 into main May 27, 2026
5 checks passed
@davydog187 davydog187 deleted the ci/dialyzer-plt-cache branch May 27, 2026 11:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant