Two related issues surfaced migrating the asobi repos to OTP 29 (consumers pin the reusable workflow at dc560fbe).
1. The eunit/ct matrix ignores version-file
The test matrix is built as:
otp: ${{ fromJSON(inputs.otp-matrix || format('["{0}"]', inputs.otp-version)) }}
and the matrix jobs call Taure/erlang-ci@main with otp-version: ${{ matrix.otp }} only (no version-file). So a consumer that sets only version-file: '.tool-versions' gets:
- analysis jobs (compile/dialyzer/xref) on the version-file OTP (e.g. 29.0.2) ✅
- eunit/ct on the
otp-version default of 28 ❌ — silently testing on the wrong OTP.
The job name even shows EUnit (OTP 28) while compile runs 29. Consumers must additionally set otp-matrix, which is easy to miss.
Suggestion: when version-file is set and otp-matrix is empty, derive the test matrix from the version-file OTP (or at least document that otp-matrix is required to pick the test OTP when using version-file).
2. _build cache key omits the OTP version
The cache is keyed on hashFiles('rebar.lock'). On an OTP bump with unchanged deps (e.g. 28→29), the cache (built on the old OTP) is restored via restore-keys and reused. Worse, test-only deps absent from rebar.lock (e.g. meck) get a stale old version restored that fails to compile on the new OTP ('catch ...' is deprecated under warnings_as_errors). We worked around it with pre-test-command: 'rm -rf _build/test/lib/meck'.
Suggestion: include the resolved OTP version in the cache key so an OTP change busts the cache.
Minimal repro
On any repo using the reusable workflow with version-file + a meck test dep: bump .tool-versions to 29.0.2 → eunit job runs on OTP 28 (issue 1); pin otp-matrix: '["29.0.2"]' → eunit then fails compiling a stale cached meck (issue 2).
Two related issues surfaced migrating the asobi repos to OTP 29 (consumers pin the reusable workflow at
dc560fbe).1. The eunit/ct matrix ignores
version-fileThe test matrix is built as:
and the matrix jobs call
Taure/erlang-ci@mainwithotp-version: ${{ matrix.otp }}only (noversion-file). So a consumer that sets onlyversion-file: '.tool-versions'gets:otp-versiondefault of28❌ — silently testing on the wrong OTP.The job name even shows
EUnit (OTP 28)while compile runs 29. Consumers must additionally setotp-matrix, which is easy to miss.Suggestion: when
version-fileis set andotp-matrixis empty, derive the test matrix from the version-file OTP (or at least document thatotp-matrixis required to pick the test OTP when usingversion-file).2.
_buildcache key omits the OTP versionThe cache is keyed on
hashFiles('rebar.lock'). On an OTP bump with unchanged deps (e.g. 28→29), the cache (built on the old OTP) is restored via restore-keys and reused. Worse, test-only deps absent fromrebar.lock(e.g.meck) get a stale old version restored that fails to compile on the new OTP ('catch ...' is deprecatedunderwarnings_as_errors). We worked around it withpre-test-command: 'rm -rf _build/test/lib/meck'.Suggestion: include the resolved OTP version in the cache key so an OTP change busts the cache.
Minimal repro
On any repo using the reusable workflow with
version-file+ amecktest dep: bump.tool-versionsto 29.0.2 → eunit job runs on OTP 28 (issue 1); pinotp-matrix: '["29.0.2"]'→ eunit then fails compiling a stale cachedmeck(issue 2).