CI: GitHub Actions in 5 Minutes
Add KindLM to your CI pipeline. Tests run on every push, block merges on failure, and produce JUnit reports your CI system already knows how to display.
Basic setup
Create .github/workflows/kindlm.yml:
name: Agent Tests
on:
push:
branches: [main]
pull_request:
jobs:
kindlm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install -g @kindlm/cli
- run: kindlm test
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
That's it. kindlm test exits with code 0 if all gates pass, code 1 if anything fails. GitHub Actions treats non-zero exit as a failed job.
Add JUnit reporting
JUnit XML is the standard format for CI test reporting. Most CI systems render it natively.
- run: kindlm test --reporter junit > junit.xml
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- uses: dorny/test-reporter@v1
if: always()
with:
name: KindLM Results
path: junit.xml
reporter: java-junit
The if: always() ensures the report is uploaded even when tests fail — so you can see which tests failed in the GitHub UI.
Multiple reporters
To get both JUnit and JSON output without running tests twice, run once with JUnit output and save the JSON report separately:
- name: Run KindLM tests
run: kindlm test --reporter junit > junit.xml
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
Or save the JSON report as an artifact:
- uses: actions/upload-artifact@v4
if: always()
with:
name: kindlm-report
path: kindlm-report.json
Compliance reports in CI
Generate EU AI Act compliance docs automatically on main branch pushes:
- name: Run tests with compliance
if: github.ref == 'refs/heads/main'
run: kindlm test --compliance --reporter json > kindlm-report.json
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- uses: actions/upload-artifact@v4
if: github.ref == 'refs/heads/main'
with:
name: compliance-report
path: compliance-reports/
Multiple providers
If your config tests against multiple models, set all required API keys:
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
Custom config path
If your config file isn't at the default kindlm.yaml:
- run: kindlm test -c tests/agent-tests.yaml
Gate thresholds
Override the pass rate gate from the command line:
- run: kindlm test --gate 95
This fails the job if the overall pass rate drops below 95%, regardless of what's in the YAML config.
Caching
KindLM has no build step, but you can cache the npm install:
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm install -g @kindlm/cli
Full example
name: Agent Tests
on:
push:
branches: [main]
pull_request:
jobs:
kindlm:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install -g @kindlm/cli
- name: Run agent tests
run: kindlm test --reporter junit > junit.xml
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Upload test report
uses: dorny/test-reporter@v1
if: always()
with:
name: KindLM Results
path: junit.xml
reporter: java-junit
- name: Generate compliance report
if: github.ref == 'refs/heads/main'
run: kindlm test --compliance
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Upload compliance artifacts
uses: actions/upload-artifact@v4
if: github.ref == 'refs/heads/main'
with:
name: compliance-reports
path: compliance-reports/
GitLab CI
kindlm:
image: node:20
script:
- npm install -g @kindlm/cli
- kindlm test --reporter junit > junit.xml
artifacts:
reports:
junit: junit.xml
variables:
OPENAI_API_KEY: $OPENAI_API_KEY
Troubleshooting
Job times out: LLM API calls take time. Set timeout-minutes: 10 or higher. Reduce defaults.repeat in your config if tests are slow.
Rate limiting: If you run many tests concurrently, the provider may rate-limit you. Set defaults.concurrency: 2 in your config to limit parallel requests.
Flaky tests: LLM outputs are non-deterministic. Use defaults.repeat: 3 and gates.passRateMin: 0.9 to tolerate occasional variance while catching real regressions.
API key not found: Make sure the secret name in GitHub matches the apiKeyEnv value in your config. Go to Settings > Secrets and variables > Actions to add secrets.