Hive
fix(server): apply shard min/max defaults when caller passes nil
GitHub issue · Closed
Summary
--shard-max N (without --shard-min or --shard-max-duration) silently fell back to one shard per test module instead of capping at N. Users with hundreds of modules got hundreds of shards. --shard-total N was unaffected because it short-circuits before touching min/max.
Root cause
In Tuist.Shards.BinPacker.determine_shard_count/2:
min_shards = Keyword.get(opts, :min, @default_min_shards)
max_shards = Keyword.get(opts, :max, @default_max_shards)
Keyword.get/3 (and Map.get/3) only applies the default when the key is missing, not when its value is nil.
The shards controller (lib/tuist_web/controllers/api/shards_controller.ex) builds the params map with Map.get(body_params, :shard_min) etc., so any CLI flag the user did not pass arrives as a present-with-nil key. By the time Shards.create_shard_plan/2 calls the bin-packer, both min: and max: may be nil, and the documented defaults never apply.
The fall-through true -> branch then evaluates:
max_shards |> max(min_shards) |> min(unit_count)
# 2 |> max(nil) |> min(144)
In Elixir’s term ordering, atoms sort greater than numbers, so max(2, nil) == nil and min(nil, 144) == 144. The user’s --shard-max 2 request quietly produced 144 shards.
Fix
Replace Keyword.get(opts, :min, @default_min_shards) with Keyword.get(opts, :min) || @default_min_shards (same for :max) so nil and missing collapse to the same default. This makes the function match its own documentation and removes the footgun for any caller that forwards optional fields through a map.
Considered fixing only at the controller boundary (filter out nil values, or use || there), but the algorithmic API itself is the more durable place to be tolerant — there are several call sites and any future ones would re-hit the same trap.
Validation
- New regression test in
test/tuist/shards/bin_packer_test.exsexercises the exact failure mode:min: nil, max: 2over 144 units → 2 shards (was 144)min: nil, max: nilover 144 units → 10 shards (the documented default)
- Existing
BinPackertest suite still passes (19 tests, 0 failures).
Test plan
-
mix test test/tuist/shards/bin_packer_test.exspasses - Manually verify
tuist test --shard-max 2(no other shard flags) produces a 2-shard plan
🤖 Generated with Claude Code
No GitHub comments yet.