Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,29 @@ on:
itself queries. Pair it with run-hex-advisory-check.
type: boolean
default: false
run-prod-compile:
description: |
Additionally compile with `MIX_ENV=prod`, to catch breakage that only
exists in the production build.

Worth turning on for anything that ships a Mix release. A release
evaluates `config/config.exs` at *build* time under `MIX_ENV=prod`,
which is a path the normal dev/test compile never touches — a missing
`config/prod.exs`, a bad `import_config`, or code that only compiles
under dev/test all pass CI and then fail when the image is built.

Where a repo builds its image on `main` only (a common pattern, to
keep PR-controlled Dockerfiles off self-hosted runners), that failure
lands *after* merge, so `main` goes red on a diff that already passed
review. This check moves it to PR time for a fraction of the cost of
a full image build.

Not `--warnings-as-errors`: the goal is "does the production build
work", not warning hygiene, and prod-only warnings in a dependency
shouldn't red-line a consumer. `_build` is already cached, so the
extra prod tree costs a full compile only when the lockfile changes.
type: boolean
default: false
run-hex-advisory-check:
description: |
Fail the build when Hex's own resolver reports a security advisory
Expand Down Expand Up @@ -212,6 +235,17 @@ jobs:
- name: Compile (warnings as errors)
run: mix compile --warnings-as-errors

- name: Compile (MIX_ENV=prod)
# Exercises the production build path: config.exs is re-evaluated with
# config_env() == :prod, so a missing config/prod.exs or a broken
# import_config fails here rather than in a post-merge image build.
if: inputs.run-prod-compile
env:
MIX_ENV: prod
run: |
mix deps.get
mix compile

- name: Format check
if: inputs.run-format-check
run: mix format --check-formatted
Expand Down
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,36 @@ project uses [SemVer](https://semver.org/) for the `vMAJOR.MINOR.PATCH` tags.

## [Unreleased]

### Added

- `elixir.yml` — `run-prod-compile`, which additionally compiles with
`MIX_ENV=prod`.

A Mix release evaluates `config/config.exs` at *build* time under
`config_env() == :prod`, a path the normal dev/test compile never touches.
So a missing `config/prod.exs`, a broken `import_config`, or code that only
compiles under dev/test passes CI green and then fails when the image is
built.

Found in a consumer repo where the image build had **never** succeeded —
every merge to `main` for months failed with
`could not read file "/app/config/prod.exs"`, while every PR was green. The
image build was gated `if: github.ref == 'refs/heads/main'` (a reasonable
pattern, keeping PR-controlled Dockerfiles off self-hosted runners), so the
failure always landed post-merge on a diff that had already passed review,
at the end of the run behind `needs:`. Nothing was watching the one place it
showed up.

This check moves that class of failure to PR time for a fraction of the cost
of a full image build. Deliberately not `--warnings-as-errors` — the
question is whether the production build *works*, not warning hygiene, and a
prod-only warning in a dependency shouldn't red-line a consumer. `_build` is
already cached, so the extra prod tree costs a full compile only when the
lockfile changes.

Off by default, like the other opt-in gates. Worth enabling for anything
that ships a release.

## [2.16.0] - 2026-08-24

### Added
Expand Down
Loading