Multi-Language Coverage
For repositories with multiple programming languages (e.g., PHP backend + JavaScript frontend), GitAuto can track coverage for each language separately. Each language runs its own test suite and uploads coverage as a separate artifact.
How It Works
- Each language runs tests in a separate CI job
- Each job uploads its coverage with a unique artifact name
- GitAuto processes all coverage artifacts and combines them
- Files are tagged with their detected language based on file extension
Supported Artifact Patterns
GitAuto automatically detects coverage artifacts using these patterns (case-insensitive):
GitHub Actions
Artifact namemust contain "coverage" or "lcov.info":
php-coverage,js-coverage- Language prefixcoverage-backend,coverage-frontend- Coverage prefixphp-lcov.info,js-lcov.info- LCOV suffixcoverage-report- Single language default
CircleCI
Artifact pathmust contain "coverage" or "lcov.info":
php/lcov.info,js/lcov.info- Directory-based (recommended)coverage/php/lcov.info,coverage/js/lcov.info- Nested directoriesphp-coverage/lcov.info,js-coverage/lcov.info- Named directories
Workflow Organization
Recommended: Separate Workflow Files
Use separate workflow files with path filters for each language. This saves CI time by only running tests when relevant files change.
name: PHP Tests
on:
push:
branches: [main]
paths: ['**/*.php']
pull_request:
branches: [main]
paths: ['**/*.php', '!.github/workflows/**']
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
coverage: xdebug
- name: Install dependencies
run: composer install
# PR: tests only, Push: tests with coverage
- name: Run tests
if: github.event_name == 'pull_request'
run: vendor/bin/phpunit
- name: Run tests with coverage
if: github.event_name == 'push'
run: vendor/bin/phpunit --coverage-clover coverage/clover.xml
- name: Convert to LCOV
if: github.event_name == 'push'
uses: andstor/clover2lcov-action@v1
with:
src: coverage/clover.xml
dst: coverage/lcov.info
- name: Upload coverage
if: github.event_name == 'push'
uses: actions/upload-artifact@v6
with:
name: php-coverage
path: coverage/lcov.info
name: JavaScript Tests
on:
push:
branches: [main]
paths: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx']
pull_request:
branches: [main]
paths: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx', '!.github/workflows/**']
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
# PR: tests only, Push: tests with coverage
- name: Run tests
if: github.event_name == 'pull_request'
run: npm test
- name: Run tests with coverage
if: github.event_name == 'push'
run: npm test -- --coverage
- name: Upload coverage
if: github.event_name == 'push'
uses: actions/upload-artifact@v6
with:
name: js-coverage
path: coverage/lcov.info
Why !.github/workflows/** on PRs Only?
On pull requests, we exclude workflow file changes to keep the setup PR minimal. When you're adding or editing a workflow file, pre-existing test failures in your codebase are irrelevant and shouldn't block the PR. On push(after merge to main), we don't exclude them so that the initial coverage report gets generated on your target branch.
Alternative: Single Workflow File
If you prefer a single file, use separate jobs per language. Each job has its own isolated workspace, so both can output to the same path (e.g., coverage/lcov.info) without conflict - but they need unique artifact names since they share the same workflow run:
name: Multi-Language Coverage
on:
push:
branches:
- main
workflow_dispatch:
# Auto-cancel outdated runs on the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
php-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
coverage: xdebug
- name: Install dependencies
run: composer install --prefer-dist --no-progress
- name: Run tests with coverage
run: composer test
- name: Convert Clover to LCOV format
uses: andstor/clover2lcov-action@v1
with:
src: coverage/clover.xml
dst: coverage/lcov.info
- name: Upload PHP coverage
uses: actions/upload-artifact@v6
with:
name: php-coverage
path: coverage/lcov.info
js-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests with coverage
run: npm test -- --coverage
- name: Upload JS coverage
uses: actions/upload-artifact@v6
with:
name: js-coverage
path: coverage/lcov.infoKey Points
- Each job runs in its own workspace - output paths don't conflict between jobs
- In a single workflow file, artifact names must be unique since jobs share the same workflow run. Separate workflow files don't have this constraint (each run has its own artifact namespace), but unique names are still recommended for clarity.
- Artifact names must contain "coverage" or "lcov.info" for GitAuto to detect them
- Without path filters, both jobs run on every push (less efficient)
Chart Visualization
The Coverage Charts page handles multi-language repositories with separate charts:
- Per-language charts:Each language gets its own chart (e.g., "my-repo (PHP)" and "my-repo (JavaScript)")
- Total coverage:The "Total Coverage" chart aggregates all languages across all repos using weighted averages based on lines of code
- Single-language repos: Repos with only one language show without the language suffix
See Coverage Charts for more details on interpreting your coverage data.
Language Detection
GitAuto automatically detects the language of each file based on its extension within the LCOV report:
.phpfiles are tagged as PHP.js,.ts,.tsxfiles are tagged as JavaScript.pyfiles are tagged as Python
This means you can filter and view coverage by language in the Coverage Dashboard.
Multi-Language Setup Challenges?
Setting up coverage for multiple languages can be tricky with different test frameworks and output formats. We're happy to help you get everything working together!
Contact us and let's get your multi-language coverage set up!