Skip to content

chore: upgrade to Elixir 1.19.5 / OTP 28.5.0.5 across CI, Docker, and local env - #34

Merged
bashar-qassis merged 1 commit into
mainfrom
chore/beam-1.19.5-otp28
Aug 20, 2026
Merged

bashar-qassis merged 1 commit into
mainfrom
chore/beam-1.19.5-otp28

Conversation

@bashar-qassis

@bashar-qassis bashar-qassis commented Aug 19, 2026 •

Copy link
Copy Markdown
Member

Aligns CI, the Docker builds, and the local toolchain on Elixir 1.19.5 / Erlang-OTP 28.5.0.5. The production image stays Alpine-based.

Why

The three surfaces had drifted apart:

Surface Before After
.tool-versions elixir 1.18.4-otp-28 / erlang 28.0.2 elixir 1.19.5-otp-28 / erlang 28.5.0.5
.github/workflows/ci.yml 1.18.4 / OTP "27" 1.19.5 / OTP "28.5"
Dockerfile (prod) 1.18.4 / 27.3.4.9 / alpine 3.21.6 1.19.5 / 28.5.0.5 / alpine 3.23.5
Dockerfile.dev elixir:1.18-otp-28-alpine elixir:1.19.5-otp-28-alpine

dev-build.yml needs no change — it builds through Dockerfile and inherits the bump. docker-compose.*.yml only pin postgres/caddy/minio.

Version choices

OTP 28.5.0.5 — newest patch of the 28 line. OTP has no formal LTS, so newest-patch-of-the-line is the stable choice.

The bump off 28.0.2 is required, not cosmetic: Elixir 1.19 officially supports OTP 28.1+, and running 1.19.5 on 28.0.2 emits

warning! Erlang/OTP 28.0 detected.
Regexes will be re-compiled from source at runtime, which will cause degraded performance.

because of the new OTP 28 regex representation. Gone on 28.5.0.5.

Alpine 3.23.5 — Alpine has no LTS either; each release gets ~2 years. The current 3.21 pin reaches EOL Nov 2026, so this moves to 3.23 (supported to ~Nov 2027) rather than taking 3.21.7 or jumping to the newest 3.24.1.

CI pins OTP as "28.5", not a bare "28". The Dialyzer PLT cache key is derived from setup-beam's resolved otp-version output, so a loose pin silently invalidates the PLT cache whenever the runner image's resolution changes.

Breaking-change review

Checked the Elixir 1.19 hard deprecations against this codebase — no application code changes were needed:

  • Struct update syntax (%URI{uri | path: ...}) now requires the variable to pattern-match on the struct first — no occurrences in lib/ or test/.
  • Regexes can no longer be defstruct field defaults (new OTP 28 regex representation) — no ~r in any defstruct.
  • Comma-separated mix do foo, bar — not used.
  • :preferred_cli_env / :preferred_cli_target in def project — not present.
  • Logger :backends config — not present.
  • on_undefined_variable: :warn, Kernel.ParallelCompiler return_diagnostics, mix compile.protocols — not used.
  • File.cp/3 callback form — the only call site (lib/kith/storage/local.ex:18) is the 2-arity form.

mix.lock is unchanged — no dependency needed updating for 1.19.

Verification

Run locally on Elixir 1.19.5 / OTP 28.5.0.5:

Gate Result
mix compile --warnings-as-errors --all-warnings ✅ 0 warnings in app code (2 remaining are dep-owned: tesla, one registry module)
mix format --check-formatted ✅
mix credo --strict ✅ 2998 mods/funs, no issues
mix sobelow --config ✅ exit 0, same pre-existing low-confidence findings as main
mix dialyzer ✅ passed — 2 errors, both pre-existing .dialyzer_ignore.exs skips
mix test ✅ 1159 tests, 0 failures
docker build ✅ verified on CI, not locally — see below

And on CI (run) — all checks green:

Job Result
test ✅ pass (5m25s)
dialyzer ✅ pass (8m59s)
docker-build ✅ pass (5m55s) — image builds on alpine 3.23.5, kith:ci version runs, container UID is 1000

The Docker build could not be run on the authoring machine: its Docker daemon cannot currently reach the registry (even a 13 KB hello-world pull times out, while host curl to auth.docker.io returns 200 in 0.27s). That is a local Docker Desktop networking fault, not a Dockerfile problem — CI's docker-build job builds the image cleanly and passes both of its runtime assertions.

Out of scope

mix hex.audit reports 4 advisories against plug 1.19.1, one HIGH (CVE-2026-54892, quadratic-time nested param decoding). These are pre-existing on main and unrelated to this bump — mix.lock is untouched here. Worth a separate PR.

… local env

Versions had drifted apart across the three surfaces: .tool-versions ran
OTP 28.0.2, CI ran OTP 27, and the production Dockerfile ran OTP 27.3.4.9.
Align all of them on Elixir 1.19.5 / OTP 28.5.0.5.

Elixir 1.19 officially supports OTP 28.1+. Running 1.19.5 on the previously
pinned 28.0.2 emits "Regexes will be re-compiled from source at runtime,
which will cause degraded performance" because of the new OTP 28 regex
representation, so the .tool-versions bump off 28.0.2 is required rather
than cosmetic.

Alpine moves 3.21.6 -> 3.23.5. Alpine 3.21 reaches EOL in Nov 2026; 3.23 is
supported to Nov 2027. The image stays Alpine-based.

CI pins OTP as "28.5" rather than a bare "28" so the Dialyzer PLT cache key,
which is derived from setup-beam's resolved otp-version output, stays stable
across runner image updates.

No application code changes were needed. Checked against the 1.19 hard
deprecations: no struct-update syntax without a preceding pattern match, no
regexes as defstruct defaults, no comma-separated `mix do`, no
:preferred_cli_env/:preferred_cli_target in mix.exs, no Logger :backends
config, and the sole File.cp call is the 2-arity form. mix.lock is unchanged
-- no dependency needed updating for 1.19.

Verified locally on Elixir 1.19.5 / OTP 28.5.0.5:
  mix compile --warnings-as-errors --all-warnings  0 app-code warnings
  mix format --check-formatted                     pass
  mix credo --strict                               no issues
  mix sobelow --config                             pass
  mix dialyzer                                     pass
  mix test                                         1159 tests, 0 failures

The docker build was not verified locally -- this machine's Docker daemon
cannot currently reach the registry. CI's docker-build job covers it.
@bashar-qassis
bashar-qassis merged commit 8b88ab8 into main Aug 20, 2026
4 checks passed
@bashar-qassis
bashar-qassis deleted the chore/beam-1.19.5-otp28 branch August 20, 2026 07:01
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