Introduction
The OtterWise Bash Uploader is a lightweight script that can be used in any CI environment to process and upload code coverage data to OtterWise. It's designed to be flexible, privacy-focused, and easy to integrate with your existing workflows.
This script handles several important tasks:
- Detecting your CI environment automatically (GitHub Actions, CircleCI, ChipperCI, etc.)
- Finding coverage reports in standard locations
- Gathering git information without requiring additional API access
- Removing code from coverage reports to maintain privacy
- Generating and sending patch information to track coverage changes
- Supporting multiple coverage formats and testing frameworks
Basic Usage
The simplest way to use the bash uploader is to download and run it directly in your CI pipeline:
bash <(curl -s https://raw.githubusercontent.com/getOtterWise/bash-uploader/main/uploader.sh)
This command will download and execute the latest version of the bash uploader. The script will look for coverage reports in common locations and attempt to automatically detect your CI environment.
Repository Token Setup
The OtterWise bash uploader requires a repository token for authentication. This token is unique to each repository and can be found in your repository settings page within OtterWise.
Finding Your Repository Token
- Navigate to your repository in OtterWise
- Click on "Settings" or "Setup Instructions"
- Copy the OTTERWISE_TOKENvalue displayed
Setting the Token in CI Environments
            The uploader looks for the OTTERWISE_TOKEN environment variable. Here's how to set it up in different CI providers:
        
GitHub Actions
- Navigate to your repository on GitHub
- Go to Settings → Secrets and variables → Actions
- Click New repository secret
- Name: OTTERWISE_TOKEN
- Value: Paste your token from OtterWise
- Click Add secret
Then reference it in your workflow:
- name: Upload to OtterWise
  env:
    OTTERWISE_TOKEN: ${{ secrets.OTTERWISE_TOKEN }}
  run: bash <(curl -s https://raw.githubusercontent.com/getOtterWise/bash-uploader/main/uploader.sh)
        CircleCI
Add the token as an environment variable in your CircleCI project settings:
- Go to your project settings in CircleCI
- Navigate to Environment Variables
- Click Add Variable
- Name: OTTERWISE_TOKEN
- Value: Paste your token from OtterWise
GitLab CI
Add the token as a CI/CD variable:
- Navigate to Settings → CI/CD → Variables
- Click Add variable
- Key: OTTERWISE_TOKEN
- Value: Paste your token from OtterWise
- Check Mask variable to hide it in logs
Travis CI
Add the token as an encrypted environment variable:
- Go to your repository settings in Travis CI
- Navigate to Environment Variables
- Name: OTTERWISE_TOKEN
- Value: Paste your token from OtterWise
- Ensure Display value in build log is OFF
Jenkins
Use Jenkins credentials or environment variables:
environment {
    OTTERWISE_TOKEN = credentials('otterwise-token')
}
        Local Development with nektos/act
For developers using nektos/act to test GitHub Actions locally, you can pass the secret directly:
act --secret OTTERWISE_TOKEN=your_token_here
Or create a .secrets file in your repository root:
OTTERWISE_TOKEN=your_token_here
Then run act without the --secret flag:
act
Security Note
Never commit your .secrets file to version control. Add it to your .gitignore file.
Alternative: Pass Token as Command Argument
If you prefer not to use environment variables, you can pass the token directly to the uploader:
bash <(curl -s https://raw.githubusercontent.com/getOtterWise/bash-uploader/main/uploader.sh) --repo-token your_token_here
Advanced: Organization Tokens
For advanced configurations, OtterWise also supports organization-wide tokens via the OTTERWISE_ORG_TOKEN environment variable or --org-token argument. Contact support for more information about this feature.
Privacy and Security
The OtterWise bash uploader is designed with privacy in mind:
- No source code transmission: The uploader strips all actual source code from coverage reports before sending them
- Local git operations: All git diff information is processed locally; only anonymized patch information is sent (showing only + and - symbols, not actual code)
- Optional verbosity control: Use the --quietflag to prevent sensitive information from appearing in CI logs
- No API dependencies: The script uses only git commands and doesn't require access to GitHub or other provider APIs
Privacy Guarantee
The OtterWise uploader takes a privacy-first approach by stripping all source code from the coverage reports before transmission. This means your actual code never leaves your CI environment - only metadata about coverage is sent.
CI Environment Detection
The bash uploader automatically detects and configures itself for various CI providers:
- GitHub Actions: Automatically detects repository, branch, and PR information
- CircleCI: Extracts workflow and job details
- ChipperCI: Optimized for Laravel's preferred CI service
- Travis CI: Supports legacy Travis builds
- AppVeyor: Extracts build and job information
- Jenkins: Detects common Jenkins environment variables
Coming soon: Support for GitLab CI, TeamCity, Heroku CI, Azure Pipelines, and Bitbucket CI.
Configuration Options
The OtterWise uploader has numerous options to customize its behavior:
Basic Options
| Option | Description | 
|---|---|
| --file | Specify a custom path to your coverage file if not in the default location | 
| --repo-token | Your OtterWise repository token (if not set as environment variable) | 
| --org-token | Your OtterWise organization token (optional, for advanced configurations) | 
| --endpoint | Custom API endpoint (defaults to https://otterwise.app/ingress/upload) | 
| --base-dir | Base directory of your project (defaults to current working directory) | 
| --quiet | Reduce verbose output for cleaner CI logs | 
| --fail-on-errors | Make the CI pipeline fail if coverage upload fails | 
Advanced Options
| Option | Description | 
|---|---|
| --mutation-file | Path to mutation testing results file (from Infection PHP) | 
| --type-coverage-file | Path to type coverage results (from tools like Pest PHP TypeCoverage plugin) | 
| --flag | Add a flag to group your coverage reports (useful for monorepos or different test suites) | 
Example Usage Scenarios
Basic Usage with Default Settings
# Using environment variable for authentication export OTTERWISE_TOKEN=your-repo-token bash <(curl -s https://raw.githubusercontent.com/getOtterWise/bash-uploader/main/uploader.sh)
Specifying a Custom Coverage File Location
bash <(curl -s https://raw.githubusercontent.com/getOtterWise/bash-uploader/main/uploader.sh) \ --repo-token your-repo-token \ --file ./path/to/custom/coverage.xml
Using with Multiple Coverage Types
bash <(curl -s https://raw.githubusercontent.com/getOtterWise/bash-uploader/main/uploader.sh) \ --repo-token your-repo-token \ --file ./coverage/clover.xml \ --type-coverage-file ./pest-type-coverage.json \ --mutation-file ./infection.json
Using Flags for Monorepos or Multiple Test Suites
# Run for backend tests bash <(curl -s https://raw.githubusercontent.com/getOtterWise/bash-uploader/main/uploader.sh) \ --repo-token your-repo-token \ --file ./backend/coverage/clover.xml \ --flag backend # Run separately for frontend tests bash <(curl -s https://raw.githubusercontent.com/getOtterWise/bash-uploader/main/uploader.sh) \ --repo-token your-repo-token \ --file ./frontend/coverage/lcov.info \ --flag frontend
Automatic Coverage File Detection
If you don't specify a coverage file location, the uploader will automatically look for coverage reports in standard locations:
- build/logs/clover.xml(PHPUnit and Pest default)
- otterwise-coverage.xml(Custom file specifically for OtterWise)
- build/logs/cobertura.xml(Python and Java coverage tools)
- coverage/lcov.info(JavaScript/TypeScript coverage tools)
Additionally, the script will automatically look for type coverage in standard locations when applicable.
Troubleshooting
If you encounter issues with the bash uploader, try these troubleshooting steps:
Common Issues and Solutions
| Issue | Solution | 
|---|---|
| Coverage file not found | 
 | 
| ERROR: No repo_token or org_token provided | Symptoms: Error messages like "No repo_token or org_token provided" 
 | 
| Authentication errors / Invalid token | Symptoms: "Invalid token", "Unauthorized", or HTTP 401/403 errors 
 | 
| Git diff issues | 
 | 
| CI detection issues | 
 | 
Debugging
            To get more detailed information about what the uploader is doing, remove the --quiet flag or add verbose logging:
        
# Run without --quiet to see full debug output bash <(curl -s https://raw.githubusercontent.com/getOtterWise/bash-uploader/main/uploader.sh) --repo-token your-repo-token
Supported Coverage Formats
The bash uploader works with the following coverage report formats:
- Clover XML (PHPUnit, Pest, and other PHP testing frameworks)
- LCOV (JavaScript, TypeScript, and front-end frameworks)
- Cobertura XML (Python, Java, and other languages)
For more details about supported formats and what data is extracted from each, see the Supported Formats page.
Advanced Metadata Collection
Beyond basic code coverage, the OtterWise bash uploader can collect and report additional metrics:
- Type Coverage: From Pest PHP's type coverage plugin
- Mutation Testing: From tools like Infection PHP
These additional metrics help create a more comprehensive view of your code quality beyond simple line coverage.
CI-Specific Integration Guides
While the bash uploader works with any CI system, we have detailed integration guides for popular CI providers:
For further assistance, contact OtterWise support or check our example repository which contains fully working CI configuration examples.