Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
92d8ea0
Remove Redis dependency, use local JSON file for timing storage
claude Jan 4, 2026
8293261
Add GitHub Actions tests with dummy test files
claude Jan 4, 2026
90acc61
Add workflow diagram to README
claude Jan 4, 2026
7cbb4fe
Fix integration test to properly simulate CI workflow
claude Jan 4, 2026
e4625b8
Fix integration test to not run all tests in prepare phase
claude Jan 4, 2026
a064866
Simplify worker jobs - no fairsplice deps needed
claude Jan 4, 2026
15e9f5c
Make --timings-file an explicit required parameter
claude Jan 8, 2026
5ff29e2
Make --timings-file an explicit required parameter
claude Jan 8, 2026
6c84ca0
Merge remote-tracking branch 'origin/claude/remove-redis-dependency-a…
claude Jan 8, 2026
ecb03aa
Add merge command to combine JUnit results from parallel workers
claude Jan 10, 2026
7a2e5a5
Remove save command - merge handles all use cases
claude Jan 10, 2026
2bd6309
Add GitHub Action with automatic caching
claude Jan 10, 2026
bc18180
Update CI to test GitHub Action with uses: ./
claude Jan 10, 2026
ac37b60
Fix action to run local code instead of npm package
claude Jan 10, 2026
a57b88a
Fix fileStorage tests to use explicit file path
claude Jan 10, 2026
a37b50b
Add convert command and refactor architecture
claude Jan 10, 2026
b714ca9
Rename convert --out to --to for consistency with --from
claude Jan 10, 2026
3019ec7
Add cache-key input for multi-workflow support
claude Jan 10, 2026
f268bdf
Make cache-key required and fix cache restore
cosmith Jan 11, 2026
3e8d5fa
Trigger CI to test cache restore
cosmith Jan 11, 2026
bb0ebb1
docs: update README with convert command and fix action schema
cosmith Jan 11, 2026
711cd12
fix: don't fail merge when no timing files found
cosmith Jan 11, 2026
b663e47
fix: don't fail convert when input file doesn't exist
cosmith Jan 11, 2026
f9b3d60
fix: pin bun version to 1.3.5
cosmith Jan 11, 2026
5d0ce25
fix: pin bun to 1.2.23 for dashdoc compatibility
cosmith Jan 11, 2026
230d1a5
feat: add path-prefix option to convert command
cosmith Jan 11, 2026
9434361
fix: add run_attempt to cache key for re-run support
cosmith Jan 11, 2026
c94e522
docs: add path-prefix option to README
cosmith Jan 12, 2026
0842c19
docs: recommend computing splits once for consistent re-runs
cosmith Jan 14, 2026
481cb7f
docs: recommend computing splits once for consistent re-runs (#3)
cosmith Jan 14, 2026
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
95 changes: 95 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: Tests

on:
push:
branches: [main, 'claude/**']
pull_request:
branches: [main]

jobs:
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v2

- name: Install dependencies
run: bun install

- name: Run unit tests
run: bun test src/

# Integration test using the GitHub Action
test:
name: Test Worker ${{ matrix.index }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
index: [0, 1, 2]
steps:
- uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v2

- name: Install dependencies
run: bun install

- name: Split tests
id: split
uses: ./
with:
command: split
pattern: 'tests/dummy/*.test.ts'
total: 3
index: ${{ matrix.index }}
cache-key: integration-tests

- name: Show assigned tests
run: echo "Worker ${{ matrix.index }} running:${{ steps.split.outputs.tests }}"

- name: Run tests
if: steps.split.outputs.tests != ''
run: bun test ${{ steps.split.outputs.tests }} --reporter=junit --reporter-outfile=junit-${{ matrix.index }}.xml

- name: Convert JUnit to timing JSON
if: steps.split.outputs.tests != ''
uses: ./
with:
command: convert
from: junit-${{ matrix.index }}.xml
to: timing-${{ matrix.index }}.json

- name: Upload timing artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: timing-${{ matrix.index }}
path: timing-${{ matrix.index }}.json

save-timings:
name: Save Timings
needs: test
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/download-artifact@v4

- name: Show downloaded artifacts
run: find . -name "*.json" -type f

- name: Merge timings
uses: ./
with:
command: merge
prefix: 'timing-*/timing-'
cache-key: integration-tests

- name: Show saved timings
run: cat .fairsplice-timings.json
57 changes: 57 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Fairsplice is a TypeScript/Bun CLI tool and GitHub Action that optimizes test distribution across parallel workers. It provides CircleCI-style test splitting based on historical timing data for GitHub Actions.

## Commands

```bash
# Run locally
bun run index.ts

# Run all tests
bun test

# Run tests in src directory
bun test src/

# Run a specific test file
bun test src/lib/splitFiles.test.ts

# Compile to standalone binary
bun build ./index.ts --compile --outfile fairsplice
```

## Architecture

**Entry Point**: `index.ts` - CLI with three commands: `split`, `convert`, `merge`

**Source Structure**:
- `src/commands/` - CLI command implementations
- `split.ts` - Distributes test files across workers using bin packing
- `merge.ts` - Aggregates timing JSON files and updates history
- `convert.ts` - Converts JUnit XML to timing JSON
- `src/lib/` - Core algorithms
- `splitFiles.ts` - Greedy bin packing algorithm (assigns heaviest tests first to balance workload)
- `junit.ts` - JUnit XML parser using `fast-xml-parser`
- `average.ts` - Timing averaging utility
- `src/backend/` - Storage layer
- `fileStorage.ts` - JSON-based timing persistence with rolling window of last 10 timings per file
- `src/config.ts` - Constants (`NUMBER_OF_TIMINGS_TO_KEEP=10`, `DEFAULT_TIMING_IF_MISSING=10000ms`)

**GitHub Action**: `action.yml` - Composite action wrapping the CLI with automatic cache handling

**Data Flow**:
1. `split` loads cached timings, globs test files, applies bin packing, outputs bucket assignments
2. Tests run in parallel workers, each outputting JUnit XML
3. `convert` transforms JUnit XML to timing JSON (one per worker)
4. `merge` aggregates timing JSONs into cached timings history

## Testing

Tests are co-located with source files (`*.test.ts`). Test fixtures for JUnit parsing are in `src/lib/fixtures/`.

The CI workflow (`.github/workflows/test.yml`) runs unit tests plus a 3-worker integration test that exercises the full split→run→convert→merge pipeline.
Loading