GitHub Action that automatically shards Scala tests for parallel execution in CI.
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3, 4] # Number of shards
steps:
- uses: actions/checkout@v4 # Pin to exact commit SHA for production
- name: Setup Scala
uses: olafurpg/setup-scala@v13 # Pin to exact commit SHA for production
- name: Shard Tests
id: shard
uses: juanri0s/sbt-sharding@v1 # Pin to exact commit SHA for production
with:
max-shards: 4
shard-number: ${{ matrix.shard }}
algorithm: round-robin
- name: Run Tests
run: sbt ${{ steps.shard.outputs.test-commands }}name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@v4 # Pin to exact commit SHA for production
- name: Setup Scala
uses: olafurpg/setup-scala@v13 # Pin to exact commit SHA for production
- name: Shard Tests
id: shard
uses: juanri0s/sbt-sharding@v1 # Pin to exact commit SHA for production
with:
max-shards: 5
shard-number: ${{ matrix.shard }}
algorithm: round-robin
test-pattern: '**/*Test.scala,**/*Spec.scala,**/*Suite.scala'
- name: Run Tests
if: steps.shard.outputs.test-files != ''
env:
JAVA_OPTS: -Xmx2g
run: |
echo "Running tests in shard ${{ matrix.shard }}/${{ steps.shard.outputs.total-shards }}"
sbt ${{ steps.shard.outputs.test-commands }}| Input | Description | Required | Default |
|---|---|---|---|
max-shards |
Maximum number of shards to split tests into | Yes | - |
shard-number |
Current shard number (1-indexed). Should match matrix.shard when using matrix strategy. Defaults to 1. |
No | 1 |
algorithm |
Sharding algorithm to use | No | round-robin |
test-pattern |
Comma-separated glob patterns for test files | No | **/*Test.scala,**/*Spec.scala,**/Test*.scala,**/Spec*.scala |
project-path |
Optional project directory path to filter test files. If provided, only test files within this directory will be included. | No | - |
| Output | Description |
|---|---|
total-shards |
Total number of shards created |
test-files |
Comma-separated list of test files to run in this shard |
test-commands |
SBT commands to run tests for this shard (e.g., testOnly com.example.Test1 testOnly com.example.Test2) |
shard-matrix |
JSON array of shard numbers for use in GitHub Actions matrix (e.g., [1,2,3]) |
The action sets these environment variables for convenience:
SBT_TEST_FILES: Comma-separated list of test filesSBT_TEST_COMMANDS: SBT commands to run tests
Note: To set environment variables for your test execution, use the env key in your GitHub Actions workflow step, not this action.
Distributes test files evenly across shards using round-robin distribution.
Distributes tests based on estimated complexity to balance execution time across shards.
Complexity factors:
- Property tests: +3 points
- Integration/container/E2E tests: +4 points
- Unit tests: -1 point (minimum 1)
- Files with many tests (>20): +2 points, (>10): +1 point
- Large files (>5000 chars): +1 point
Tests are sorted by complexity (highest first) and distributed using a greedy bin-packing algorithm to balance total complexity across shards.
- Node.js 24+ (automatically provided by GitHub Actions)
- pnpm 10+ (managed via corepack)
- SBT project structure with test files in
src/test/scala/or similar
Before using this action (or when developing), you need to build it:
corepack enable
pnpm install
pnpm run buildFor monorepos with multiple projects, use the project-path input to scope test discovery to a specific project directory:
name: Test Monorepo
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
project: [projectA, projectB]
shard: [1, 2, 3]
steps:
- uses: actions/checkout@v4 # Pin to exact commit SHA for production
- name: Setup Scala
uses: olafurpg/setup-scala@v13 # Pin to exact commit SHA for production
- name: Shard Tests
id: shard
uses: juanri0s/sbt-sharding@v1 # Pin to exact commit SHA for production
with:
max-shards: 3
shard-number: ${{ matrix.shard }}
project-path: ${{ matrix.project }}
test-pattern: '**/*Test.scala,**/*Spec.scala'
- name: Run Tests
if: steps.shard.outputs.test-files != ''
run: sbt ${{ steps.shard.outputs.test-commands }}MIT