# Generated projects test sharding {#generated-projects-test-sharding}
::: warning REQUIREMENTS
- A Tuist generated project
- A Tuist account and project
- Test Insights configured (for optimal shard balancing)
:::
Test sharding for generated projects uses `tuist test` for both the build and test phases.
## How it works {#how-it-works}
Test sharding follows a two-phase workflow:
1. **Build phase:** Tuist enumerates your tests and creates a **shard plan** on the server. The server uses historical test timing data from the last 30 days to distribute tests across shards so each shard takes roughly the same amount of time. The build phase outputs a **shard matrix** that your CI system uses to spawn parallel runners.
2. **Test phase:** Each CI runner receives a **shard index** and executes only the tests assigned to that shard.
## Build phase {#build-phase}
Generate your project, build your tests, and create a shard plan:
```sh
tuist test --shard-total 5
```
This command:
1. Generates the Xcode project from your manifests
2. Builds your tests
3. Creates a shard plan on the Tuist server using historical timing data
4. Uploads the `.xctestproducts` bundle for use by shard runners
5. Outputs a shard matrix for your CI system
6. Persists the selective testing graph (if applicable) so shard runners don't need to regenerate the project
### Build options {#build-options}
| Flag | Environment variable | Description |
|------|---------------------|-------------|
| `--shard-max ` | `TUIST_TEST_SHARD_MAX` | Maximum number of shards. Used with `--shard-max-duration` to cap the shard count |
| `--shard-min ` | `TUIST_TEST_SHARD_MIN` | Minimum number of shards |
| `--shard-total ` | `TUIST_TEST_SHARD_TOTAL` | Exact number of shards (mutually exclusive with `--shard-min`/`--shard-max`) |
| `--shard-max-duration ` | `TUIST_TEST_SHARD_MAX_DURATION` | Target maximum duration per shard in milliseconds |
| `--shard-granularity ` | `TUIST_TEST_SHARD_GRANULARITY` | `module` (default) distributes entire test modules across shards; `suite` distributes individual test classes for finer-grained balancing |
| `--shard-reference [` | `TUIST_SHARD_REFERENCE` | Unique identifier for the shard plan (auto-derived on supported CI providers) |
## Test phase {#test-phase}
Each shard runner executes its assigned tests:
```sh
tuist test
```
### Test options {#test-options}
| Flag | Environment variable | Description |
|------|---------------------|-------------|
| `--shard-index ` | `TUIST_SHARD_INDEX` | Zero-based index of the shard to execute |
| `--shard-reference ][` | `TUIST_SHARD_REFERENCE` | Unique identifier for the shard plan (auto-derived on supported CI providers) |
Tuist downloads the `.xctestproducts` bundle and filters it to include only the tests assigned to that shard.
::: tip SELECTIVE TESTING
Test sharding works seamlessly with selective testing. The selective testing graph is persisted during the build phase and restored for each shard, so runners don't need to regenerate the project.
:::
## Continuous integration {#continuous-integration}
Tuist automatically detects the following CI providers:
- [GitHub Actions](#github-actions)
- [GitLab CI](#gitlab-ci)
- [CircleCI](#circleci)
- [Buildkite](#buildkite)
- [Codemagic](#codemagic)
- [Bitrise](#bitrise)
For other providers, refer to the `.tuist-shard-matrix.json` file to set up parallel jobs.
### GitHub Actions {#github-actions}
Use a matrix strategy to run shards in parallel:
```yaml
name: Tests
on: [pull_request]
jobs:
build:
name: Build test shards
runs-on: macos-latest
outputs:
matrix: ${{ steps.build.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- uses: jdx/mise-action@v2
- run: tuist auth login
- id: build
run: tuist test --shard-total 5
test:
name: "Shard #${{ matrix.shard }}"
needs: build
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
shard: ${{ fromJson(needs.build.outputs.matrix).shard }}
env:
TUIST_SHARD_INDEX: ${{ matrix.shard }}
steps:
- uses: actions/checkout@v4
- uses: jdx/mise-action@v2
- run: tuist auth login
- run: tuist test
```
### GitLab CI {#gitlab-ci}
Tuist generates a `.tuist-shard-child-pipeline.yml` that you trigger as a [child pipeline](https://docs.gitlab.com/ee/ci/pipelines/downstream_pipelines.html#parent-child-pipelines). Define a `.tuist-shard` template job that the generated shard jobs extend:
```yaml
# .gitlab-ci.yml
stages:
- build
- test
build-shards:
stage: build
tags: [macos]
script:
- tuist auth login
- tuist test --shard-total 5
artifacts:
paths:
- .tuist-shard-child-pipeline.yml
test-shards:
stage: test
needs: [build-shards]
trigger:
include:
- artifact: .tuist-shard-child-pipeline.yml
job: build-shards
strategy: depend
```
```yaml
# .gitlab/shard-template.yml
.tuist-shard:
tags: [macos]
script:
- tuist auth login
- tuist test
```
### CircleCI {#circleci}
Tuist generates a `.tuist-shard-continuation.json` with parameters for the [continuation orb](https://circleci.com/developer/orbs/orb/circleci/continuation):
```yaml
# .circleci/config.yml
version: 2.1
setup: true
orbs:
continuation: circleci/continuation@1
jobs:
build-shards:
macos:
xcode: "16.0"
steps:
- checkout
- run:
name: Build and plan shards
command: |
tuist auth login
tuist test --shard-total 5
- continuation/continue:
configuration_path: .circleci/continue-config.yml
parameters: .tuist-shard-continuation.json
workflows:
setup:
jobs:
- build-shards
```
```yaml
# .circleci/continue-config.yml
version: 2.1
parameters:
shard-indices:
type: string
default: ""
shard-count:
type: integer
default: 0
jobs:
test-shard:
macos:
xcode: "16.0"
parameters:
shard-index:
type: integer
steps:
- checkout
- run:
name: Run shard
command: |
export TUIST_SHARD_INDEX=<< parameters.shard-index >>
tuist auth login
tuist test
workflows:
test:
jobs:
- test-shard:
matrix:
parameters:
shard-index: [<< pipeline.parameters.shard-indices >>]
```
### Buildkite {#buildkite}
Tuist generates a `.tuist-shard-pipeline.yml` with one step per shard. Upload it with `buildkite-agent pipeline upload`:
```yaml
# pipeline.yml
steps:
- label: "Build test shards"
command: |
tuist auth login
tuist test --shard-total 5
buildkite-agent pipeline upload .tuist-shard-pipeline.yml
agents:
queue: macos
```
Each generated step has `TUIST_SHARD_INDEX` set in its environment. Add the test command to each shard step using a shared script:
```bash
# .buildkite/shard-step.sh
#!/bin/bash
tuist auth login
tuist test
```
### Codemagic {#codemagic}
Codemagic does not support dynamic matrix jobs, so define a separate workflow per shard. Tuist writes `TUIST_SHARD_MATRIX` and `TUIST_SHARD_COUNT` to the `CM_ENV` file for use within each workflow:
```yaml
# codemagic.yaml
workflows:
build-shards:
name: Build test shards
instance_type: mac_mini_m2
environment:
xcode: latest
scripts:
- name: Build and plan shards
script: |
tuist auth login
tuist test --shard-total 5
test-shard-0: &shard-workflow
name: "Shard #0"
instance_type: mac_mini_m2
environment:
xcode: latest
vars:
TUIST_SHARD_INDEX: 0
scripts:
- name: Run shard
script: |
tuist auth login
tuist test
test-shard-1:
<<: *shard-workflow
name: "Shard #1"
environment:
xcode: latest
vars:
TUIST_SHARD_INDEX: 1
test-shard-2:
<<: *shard-workflow
name: "Shard #2"
environment:
xcode: latest
vars:
TUIST_SHARD_INDEX: 2
test-shard-3:
<<: *shard-workflow
name: "Shard #3"
environment:
xcode: latest
vars:
TUIST_SHARD_INDEX: 3
test-shard-4:
<<: *shard-workflow
name: "Shard #4"
environment:
xcode: latest
vars:
TUIST_SHARD_INDEX: 4
```
### Bitrise {#bitrise}
On Bitrise, Tuist writes `.tuist-shard-matrix.json` to the `BITRISE_DEPLOY_DIR`, making it available as a build artifact for downstream pipeline stages. Use Bitrise Pipelines with pre-defined parallel workflows:
```yaml
# bitrise.yml
pipelines:
test-pipeline:
stages:
- build-stage: {}
- test-stage: {}
stages:
build-stage:
workflows:
- build-shards: {}
test-stage:
workflows:
- test-shard-0: {}
- test-shard-1: {}
- test-shard-2: {}
- test-shard-3: {}
- test-shard-4: {}
workflows:
build-shards:
steps:
- script:
title: Build and plan shards
inputs:
- content: |
tuist auth login
tuist test --shard-total 5
test-shard-0: &shard-workflow
envs:
- TUIST_SHARD_INDEX: 0
steps:
- script:
title: Run shard
inputs:
- content: |
tuist auth login
tuist test
test-shard-1:
<<: *shard-workflow
envs:
- TUIST_SHARD_INDEX: 1
test-shard-2:
<<: *shard-workflow
envs:
- TUIST_SHARD_INDEX: 2
test-shard-3:
<<: *shard-workflow
envs:
- TUIST_SHARD_INDEX: 3
test-shard-4:
<<: *shard-workflow
envs:
- TUIST_SHARD_INDEX: 4
```
::: tip
Bitrise does not support dynamic parallel job creation at runtime. Define a fixed number of shard workflows in your pipeline stages — workflows within a stage run in parallel automatically.
:::
]