From 829e8fbd4b62b09316289cd04558f99d6dbca0bc Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Tue, 26 May 2026 17:46:03 -0700 Subject: [PATCH 1/4] chore(A38): start plan --- .agents/plans/A38-dialyzer-plt-cache.md | 89 +++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 .agents/plans/A38-dialyzer-plt-cache.md diff --git a/.agents/plans/A38-dialyzer-plt-cache.md b/.agents/plans/A38-dialyzer-plt-cache.md new file mode 100644 index 0000000..9b53d47 --- /dev/null +++ b/.agents/plans/A38-dialyzer-plt-cache.md @@ -0,0 +1,89 @@ +--- +id: A38 +title: "Cache the Dialyzer PLT in CI to cut job time from ~3m30s to <60s" +issue: null +pr: null +branch: ci/dialyzer-plt-cache +base: main +status: in-progress +direction: A +--- + +## 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. + +## Out of scope + +- Changing what dialyzer actually 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. + +## Success criteria + +- [ ] `mix compile --warnings-as-errors` passes. +- [ ] `mix test` passes (no regressions). +- [ ] `mix dialyzer` passes locally (PLT builds to `priv/plts/`). +- [ ] CI workflow YAML is valid (yamllint or manual inspection). +- [ ] On first run (cache miss) the dialyzer job completes successfully. +- [ ] On second run (cache hit) the `mix dialyzer` step takes < 60s. + +## Implementation notes + +### 1. Pin PLT paths in `mix.exs` + +Add `plt_core_path` and `plt_local_path` to the `dialyzer:` keyword list so +the PLT files land in a stable, cache-able directory (`priv/plts/`): + +```elixir +dialyzer: [ + plt_add_apps: [:ex_unit], + plt_core_path: "priv/plts/core", + plt_local_path: "priv/plts/local" +] +``` + +Drop `:mix` from `plt_add_apps` — `lib/` does not reference `Mix.*` at +runtime (only `tasks/` does, and that's excluded from the production +`elixirc_paths/1`). + +### 2. Add `priv/plts/` to `.gitignore` + +The PLT files are build artefacts; they must not be committed. + +### 3. Update `.github/workflows/ci.yml` + +In the `dialyzer` job: +- Add a `Restore PLT cache` step (using `actions/cache@v4`) immediately + after `install-deps`, caching `priv/plts`. Cache key: + `${{ runner.os }}-elixir${{ matrix.elixir }}-otp${{ matrix.otp }}-plt-${{ hashFiles('**/mix.lock') }}` + with a `restore-keys` fallback keyed on OS/Elixir/OTP so a `mix.lock` + bump triggers an incremental PLT update instead of a full rebuild. +- Split the single `mix dialyzer` step into two: + - `Build PLT` — `mix dialyzer --plt` (skipped on cache hit by dialyxir's + own PLT-up-to-date check, but makes the CI log show build vs. analysis + time separately). + - `Run dialyzer` — `mix dialyzer` (fast on cache hit). + +## Verification + +```bash +mix format --check-formatted +mix compile --warnings-as-errors +mix test +mix dialyzer --plt # builds to priv/plts/ — must succeed +mix dialyzer # must exit 0 +``` + +## Risks + +- **Cache eviction**: GitHub Actions caches expire after 7 days of no use. + After eviction, the next run pays the full ~3m rebuild cost — acceptable + and self-healing. +- **PLT path mismatch**: if `mix dialyzer` writes somewhere other than + `priv/plts/`, the cache step won't capture it. Verify by running locally + and confirming the directory exists. +- **Cache storage budget**: PLT files are ~30-50 MB per Elixir/OTP + combination, well within the typical 10 GB org cache cap. From b805f710ace66a39667a4b51f86d9e2ef8124290 Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Tue, 26 May 2026 17:47:46 -0700 Subject: [PATCH 2/4] ci(dialyzer): cache PLT and split build/analyze steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .github/workflows/ci.yml | 12 ++++++++++++ .gitignore | 3 +++ mix.exs | 4 +++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abaeeec..96d5338 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -230,5 +230,17 @@ jobs: - *bootstrap-beam - *cache-deps - *install-deps + + - name: Restore PLT cache + uses: actions/cache@v4 + with: + path: priv/plts + key: ${{ runner.os }}-elixir${{ matrix.elixir }}-otp${{ matrix.otp }}-plt-${{ hashFiles('**/mix.lock') }} + restore-keys: | + ${{ runner.os }}-elixir${{ matrix.elixir }}-otp${{ matrix.otp }}-plt- + + - name: Build PLT + run: mix dialyzer --plt + - name: Run dialyzer run: mix dialyzer diff --git a/.gitignore b/.gitignore index 3924b5d..cb28075 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,9 @@ erl_crash.dump # Ignore package tarball (built via "mix hex.build"). lua-*.tar +# Dialyzer PLT files (build artefacts, cached in CI via actions/cache). +/priv/plts/ + # Temporary files, for example, from tests. /tmp/ diff --git a/mix.exs b/mix.exs index f015b0e..a5c1329 100644 --- a/mix.exs +++ b/mix.exs @@ -15,7 +15,9 @@ defmodule Lua.MixProject do # Dialyzer dialyzer: [ - plt_add_apps: [:ex_unit, :mix] + plt_add_apps: [:ex_unit], + plt_core_path: "priv/plts/core", + plt_local_path: "priv/plts/local" ], # Docs From 098449ce23384cab099ab92d1ab80b2b5b188136 Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Tue, 26 May 2026 17:48:49 -0700 Subject: [PATCH 3/4] chore(A38): mark plan as review, record PR #243 --- .agents/plans/A38-dialyzer-plt-cache.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.agents/plans/A38-dialyzer-plt-cache.md b/.agents/plans/A38-dialyzer-plt-cache.md index 9b53d47..8614c26 100644 --- a/.agents/plans/A38-dialyzer-plt-cache.md +++ b/.agents/plans/A38-dialyzer-plt-cache.md @@ -2,10 +2,10 @@ id: A38 title: "Cache the Dialyzer PLT in CI to cut job time from ~3m30s to <60s" issue: null -pr: null +pr: 243 branch: ci/dialyzer-plt-cache base: main -status: in-progress +status: review direction: A --- @@ -87,3 +87,11 @@ mix dialyzer # must exit 0 and confirming the directory exists. - **Cache storage budget**: PLT files are ~30-50 MB per Elixir/OTP combination, well within the typical 10 GB org cache cap. + +## What changed + +- `mix.exs`: added `plt_core_path: "priv/plts/core"`, `plt_local_path: "priv/plts/local"`, removed `:mix` from `plt_add_apps` +- `.gitignore`: added `/priv/plts/` +- `.github/workflows/ci.yml`: added `Restore PLT cache` step, split `mix dialyzer` into `Build PLT` + `Run dialyzer` + +PR: https://github.com/tv-labs/lua/pull/243 From f6d683635f3c56e6940e8bb283ba0f9a5355369f Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Wed, 27 May 2026 04:01:32 -0700 Subject: [PATCH 4/4] ci(dialyzer): restore :mix in plt_add_apps for Mix task files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index a5c1329..e39260f 100644 --- a/mix.exs +++ b/mix.exs @@ -15,7 +15,7 @@ defmodule Lua.MixProject do # Dialyzer dialyzer: [ - plt_add_apps: [:ex_unit], + plt_add_apps: [:ex_unit, :mix], plt_core_path: "priv/plts/core", plt_local_path: "priv/plts/local" ],