diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 47d3fd2..9a248aa 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -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 @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index e2197de..266bf86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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