Skip to content

Commit 8629443

Browse files
bougymanclaude
andauthored
feat: add mix ci task for the complete quality gate (#127)
## Summary - Adds `Mix.Tasks.Ci` (`lib/mix/tasks/ci.ex`) that runs the four quality gate steps (`deps.get`, `format --check-formatted`, `usage_rules.sync --check`, `mix test`) sequentially inside `app/`, failing fast on the first error - Updates `.github/workflows/ci.yaml` to call `mix ci` instead of the four individual steps — CI and local `mix ci` now share one code path - Adds `test/mix/tasks/ci_test.exs` verifying the correct sequence of commands is invoked (using an injected shell runner, matching the pattern from `lc_test.exs`) Closes CRY-49 ## Test plan - [x] `mix compile --warnings-as-errors` passes - [x] `mix format --check-formatted` passes - [x] `mix test` passes (12/12, including the new `Mix.Tasks.CiTest`) - [x] CI workflow updated to call `mix ci` with `working-directory: .` (repo root), overriding the job's `app/` default 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent bfc0047 commit 8629443

3 files changed

Lines changed: 68 additions & 11 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,12 @@ jobs:
4444
app/_build
4545
key: ${{ runner.os }}-mix-${{ hashFiles('app/mix.lock') }}
4646
-
47-
run: mix deps.get
48-
-
49-
run: mix format --check-formatted
50-
-
51-
# Catches drift introduced within this PR (e.g. an ash/oban bump
52-
# without re-running the sync) - see #79. The reactive auto-fix
53-
# for drift from any other source lives in its own workflow
54-
# (usage-rules-sync.yaml), not here.
55-
run: mix usage_rules.sync --check
56-
-
57-
run: mix test
47+
# mix ci covers deps.get, format --check-formatted,
48+
# usage_rules.sync --check (catches dep-bump drift, see #79),
49+
# and mix test. Runs from the repo root; cd: app/ is handled
50+
# inside the task itself.
51+
run: mix ci
52+
working-directory: .
5853

5954
conventional_commits:
6055
if: inputs.skip_commit_validation != true

lib/mix/tasks/ci.ex

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
defmodule Mix.Tasks.Ci do
2+
@shortdoc "Runs the complete quality gate against app/"
3+
4+
@moduledoc """
5+
#{@shortdoc}.
6+
7+
mix ci
8+
9+
Runs every check `.github/workflows/ci.yaml`'s `test` job runs on a pull
10+
request, in the same order, so a green `mix ci` locally predicts a green
11+
CI run — and CI itself calls this task, so there's one place to fix if
12+
either ever breaks:
13+
14+
1. `mix deps.get` — ensure deps are present
15+
2. `mix format --check-formatted` — code is formatted
16+
3. `mix usage_rules.sync --check` — usage rules are in sync with deps
17+
(catches drift introduced by a dep bump without re-running the sync;
18+
see #79)
19+
4. `mix test` — all tests pass
20+
21+
All steps run inside `app/`.
22+
"""
23+
24+
use Mix.Task
25+
26+
alias RepoTasks.Shell
27+
28+
@impl Mix.Task
29+
def run(argv) do
30+
run(argv, &Shell.run!/3)
31+
end
32+
33+
@doc false
34+
def run(_argv, shell) do
35+
shell.("mix", ["deps.get"], cd: "app")
36+
shell.("mix", ["format", "--check-formatted"], cd: "app")
37+
shell.("mix", ["usage_rules.sync", "--check"], cd: "app")
38+
shell.("mix", ["test"], cd: "app")
39+
:ok
40+
end
41+
end

test/mix/tasks/ci_test.exs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
defmodule Mix.Tasks.CiTest do
2+
use ExUnit.Case, async: true
3+
4+
alias Mix.Tasks.Ci
5+
6+
test "runs all quality gate steps in order" do
7+
caller = self()
8+
9+
shell = fn cmd, args, opts ->
10+
send(caller, {:run, cmd, args, opts})
11+
:ok
12+
end
13+
14+
assert :ok = Ci.run([], shell)
15+
16+
assert_receive {:run, "mix", ["deps.get"], [cd: "app"]}
17+
assert_receive {:run, "mix", ["format", "--check-formatted"], [cd: "app"]}
18+
assert_receive {:run, "mix", ["usage_rules.sync", "--check"], [cd: "app"]}
19+
assert_receive {:run, "mix", ["test"], [cd: "app"]}
20+
end
21+
end

0 commit comments

Comments
 (0)