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
49 changes: 49 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,52 @@ jobs:
run: nix develop -c mix format --check-formatted
- name: Run Credo
run: nix develop -c mix credo

compatibility:
# Proves the version ranges declared in mix.exs actually hold: the
# oldest and newest supported Elixir, each against the oldest and
# newest req allowed by "~> 0.5.15 or ~> 0.6". Deps are resolved
# unlocked so mix.lock cannot mask a range that does not work.
name: Elixir ${{ matrix.elixir }} / OTP ${{ matrix.otp }} / req ${{ matrix.req }}
runs-on: ubuntu-latest
env:
MIX_ENV: test
strategy:
fail-fast: false
matrix:
include:
# Floor of the supported Elixir range.
- elixir: "1.18.4"
otp: "27.3"
req: "0.5.15"
- elixir: "1.18.4"
otp: "27.3"
req: "0.6.3"
# Elixir 1.18 on OTP 28 is where the module-attribute regex
# compile error surfaced, so it gets its own entry.
- elixir: "1.18.4"
otp: "28.0"
req: "0.6.3"
# Current toolchain, matching the Nix dev shell.
- elixir: "1.20.1"
otp: "28.0"
req: "0.5.15"
- elixir: "1.20.1"
otp: "28.0"
req: "0.6.3"
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: ${{ matrix.elixir }}
otp-version: ${{ matrix.otp }}
- name: Pin req to ${{ matrix.req }}
# Resolves without mix.lock so the range declared in mix.exs is
# what actually gets exercised, and asserts the pin took effect.
run: .github/workflows/pin-req.sh '${{ matrix.req }}'
- name: Compile with warnings as errors
run: mix compile --warnings-as-errors
- name: Run tests
run: mix test
33 changes: 33 additions & 0 deletions .github/workflows/pin-req.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
# Pin the :req dependency to an exact version so a compatibility matrix
# job exercises one specific point of the range declared in mix.exs.
#
# Dependency resolution always picks the newest version a constraint
# allows, and `mix deps.update --to` is a no-op for this, so the only
# reliable way to test the floor of a range is to rewrite the constraint
# before resolving. This edit is scratch-only; CI never commits it.
set -euo pipefail

version="${1:?usage: pin-req.sh <version>}"

# Fail loudly rather than silently testing the wrong version if the
# constraint in mix.exs is ever reworded.
if ! grep -qE '\{:req, "[^"]+"\}' mix.exs; then
echo "pin-req: no {:req, \"...\"} dependency found in mix.exs" >&2
exit 1
fi

perl -pi -e 'BEGIN { $v = shift } s/\{:req, "[^"]+"\}/{:req, "$v"}/' "$version" mix.exs

grep -E '\{:req, ' mix.exs

rm -f mix.lock
mix deps.get

resolved=$(grep -oE '"req": \{:hex, :req, "[^"]+"' mix.lock | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
echo "resolved req: $resolved"

if [ "$resolved" != "$version" ]; then
echo "pin-req: expected req $version but resolved $resolved" >&2
exit 1
fi
Loading