Configuration
After installing the Unfold CI GitHub App, you need to generate an API key and configure your GitHub Actions workflow to send test results to our platform.
Generating an API Key
API keys authenticate your GitHub Actions workflows when sending test results to Unfold CI.
From the Dashboard
-
Login to Dashboard
- Visit app.unfoldci.com
- Sign in with GitHub
-
Navigate to Settings
- Click your profile icon (top-right)
- Select "Settings" or "API Keys"
-
Generate New Key
- Click "Generate New API Key"
- Give it a descriptive name (e.g., "Production Repo Key")
- Select scope:
- Repository-specific: Only for one repository
- Organization-wide: For all repos in your organization (recommended)
- Click "Create Key"
-
Copy Your Key
unfold_sk_1234567890abcdef1234567890abcdef
The key is only shown once! Copy it immediately and store it securely. You'll need it for GitHub Actions configuration.
Key Management
- View Keys: See all active keys in Settings → API Keys
- Revoke Keys: Delete compromised or unused keys anytime
- Rotate Keys: Generate new keys periodically for security
- Multiple Keys: Create separate keys for different repos/environments
Storing API Key in GitHub
Store your API key as a GitHub Secret so it's secure and accessible to workflows.
Repository-Level Secret (Single Repo)
-
Navigate to Repository Settings
- Go to your repository on GitHub
- Click "Settings" tab
-
Add Secret
- Go to "Secrets and variables" → "Actions"
- Click "New repository secret"
-
Configure Secret
- Name:
UNFOLD_API_KEY - Value: Paste your API key (e.g.,
unfold_sk_...) - Click "Add secret"
- Name:
Organization-Level Secret (Multiple Repos)
For organizations with multiple repositories:
-
Navigate to Organization Settings
- Go to your GitHub organization
- Click "Settings"
-
Add Organization Secret
- Go to "Secrets and variables" → "Actions"
- Click "New organization secret"
-
Configure Secret
- Name:
UNFOLD_API_KEY - Value: Paste your API key
- Repository access:
- Select "All repositories" or
- Choose specific repositories
- Click "Add secret"
- Name:
- Single key for all repositories
- Centralized key management
- Easier rotation and revocation
- Consistent across all projects
GitHub Actions Workflow
Configure your workflow to run tests and send results to Unfold CI.
Basic Workflow Template
Create or update .github/workflows/unfold-ci.yml:
name: Unfold CI - Test Monitoring
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup environment
# Your language-specific setup (Node.js, Python, .NET, etc.)
- name: Install dependencies
# Your dependency installation command
- name: Run tests
# Your test command (see framework examples below)
continue-on-error: true # Continue even if tests fail
- name: Send results to Unfold CI
if: always() # Run even if tests failed
env:
UNFOLD_API_KEY: ${{ secrets.UNFOLD_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
curl -X POST https://api.unfoldci.com/api/test-results \
-H "Authorization: Bearer $UNFOLD_API_KEY" \
-H "Content-Type: application/json" \
-d @test-results.json
Framework-Specific Examples
JavaScript/TypeScript - Jest
name: Unfold CI - Jest Tests
on:
push:
branches: [ main ]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run Jest tests
run: |
npm test -- \
--json \
--outputFile=test-results.json \
--testLocationInResults
continue-on-error: true
- name: Upload to Unfold CI
if: always()
run: |
curl -X POST https://api.unfoldci.com/api/test-results \
-H "Authorization: Bearer ${{ secrets.UNFOLD_API_KEY }}" \
-H "Content-Type: application/json" \
-F "file=@test-results.json" \
-F "repo=${{ github.repository }}" \
-F "branch=${{ github.ref_name }}" \
-F "commit=${{ github.sha }}" \
-F "framework=jest"
Python - pytest
name: Unfold CI - pytest Tests
on:
push:
branches: [ main ]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-json-report
- name: Run pytest
run: |
pytest \
--json-report \
--json-report-file=test-results.json \
-v
continue-on-error: true
- name: Upload to Unfold CI
if: always()
run: |
curl -X POST https://api.unfoldci.com/api/test-results \
-H "Authorization: Bearer ${{ secrets.UNFOLD_API_KEY }}" \
-H "Content-Type: application/json" \
-F "file=@test-results.json" \
-F "repo=${{ github.repository }}" \
-F "branch=${{ github.ref_name }}" \
-F "commit=${{ github.sha }}" \
-F "framework=pytest"
C#/.NET - xUnit/NUnit
name: Unfold CI - .NET Tests
on:
push:
branches: [ main ]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Run tests
run: |
dotnet test \
--no-build \
--logger "trx;LogFileName=test-results.trx" \
--logger "json;LogFileName=test-results.json"
continue-on-error: true
- name: Upload to Unfold CI
if: always()
run: |
# Find the test results file
TEST_FILE=$(find . -name "test-results.json" | head -n 1)
curl -X POST https://api.unfoldci.com/api/test-results \
-H "Authorization: Bearer ${{ secrets.UNFOLD_API_KEY }}" \
-H "Content-Type: application/json" \
-F "file=@$TEST_FILE" \
-F "repo=${{ github.repository }}" \
-F "branch=${{ github.ref_name }}" \
-F "commit=${{ github.sha }}" \
-F "framework=nunit"
Java - JUnit
name: Unfold CI - JUnit Tests
on:
push:
branches: [ main ]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Run tests
run: |
mvn test \
-Dsurefire.reportFormat=json \
-Dsurefire.useFile=false
continue-on-error: true
- name: Upload to Unfold CI
if: always()
run: |
curl -X POST https://api.unfoldci.com/api/test-results \
-H "Authorization: Bearer ${{ secrets.UNFOLD_API_KEY }}" \
-H "Content-Type: application/json" \
-F "file=@target/surefire-reports/TEST-*.xml" \
-F "repo=${{ github.repository }}" \
-F "branch=${{ github.ref_name }}" \
-F "commit=${{ github.sha }}" \
-F "framework=junit"
Workflow Best Practices
Trigger on All Branches
Monitor tests across all important branches:
on:
push:
branches:
- main
- develop
- staging
- 'release/**'
pull_request:
Run on Schedule
Catch time-based flaky tests:
on:
schedule:
- cron: '0 */6 * * *' # Every 6 hours
Always Upload Results
Use if: always() to upload even when tests fail:
- name: Upload to Unfold CI
if: always() # Critical!
run: # upload command
Include Test Context
Send additional metadata for better analysis:
-F "pr_number=${{ github.event.pull_request.number }}" \
-F "workflow_run_id=${{ github.run_id }}" \
-F "actor=${{ github.actor }}"
Verification
Test Your Setup
- Push a commit to trigger the workflow
- Check GitHub Actions tab - workflow should complete
- View Dashboard - tests should appear within seconds
- Check logs if issues occur
Workflow Succeeded?
You should see in GitHub Actions:
✅ Checkout code
✅ Setup environment
✅ Install dependencies
✅ Run tests
✅ Upload to Unfold CI ← Key step
Dashboard Updated?
Check your dashboard shows:
- ✅ Total tests count increased
- ✅ Recent test runs appear
- ✅ Repository is "Active"
- ✅ Health score calculated
Troubleshooting
API Key Issues
Problem: 401 Unauthorized or Invalid API key
Solutions:
# 1. Verify secret exists in GitHub
# Settings → Secrets → Actions → UNFOLD_API_KEY
# 2. Test key manually
curl https://api.unfoldci.com/api/health \
-H "Authorization: Bearer YOUR_KEY"
# 3. Regenerate key if compromised
# Dashboard → Settings → API Keys → Generate New
Test Results Not Appearing
Problem: Workflow succeeds but no data in dashboard
Solutions:
-
Check test results file exists
- name: Debug test file
run: |
ls -la test-results.json
head -n 20 test-results.json -
Verify API request
- name: Upload with debug
run: |
curl -v -X POST https://api.unfoldci.com/api/test-results \
-H "Authorization: Bearer ${{ secrets.UNFOLD_API_KEY }}" \
-F "file=@test-results.json" \
-F "repo=${{ github.repository }}" -
Check API response
- Look for response code (should be 200/201)
- Check for error messages in workflow logs
Workflow Fails
Problem: Upload step fails with error
Solutions:
-
Ensure
continue-on-error: trueon test step- name: Run tests
run: npm test
continue-on-error: true # Critical! -
Check network access
- Verify api.unfoldci.com is accessible
- Check for firewall/proxy issues
- Try with different runner (if self-hosted)
-
Validate test file format
- Ensure test results are valid JSON/XML
- Check framework compatibility
- Test locally first
Advanced Configuration
Custom Test Formats
If using a custom test framework:
- name: Convert to Unfold CI format
run: |
python convert-test-results.py \
--input custom-tests.xml \
--output test-results.json \
--format unfold-ci
Multiple Test Suites
Run and upload different test types:
- name: Unit tests
run: npm run test:unit
- name: Upload unit results
run: curl ... -F "suite=unit"
- name: Integration tests
run: npm run test:integration
- name: Upload integration results
run: curl ... -F "suite=integration"
Private Repositories
For self-hosted runners or private repos:
env:
UNFOLD_API_URL: https://api.unfoldci.com # Can be customized
UNFOLD_API_KEY: ${{ secrets.UNFOLD_API_KEY }}
Security Best Practices
API Key Security
- ✅ Always use GitHub Secrets - Never hardcode keys
- ✅ Rotate keys quarterly - Generate new keys regularly
- ✅ Use repo-specific keys - Limit scope where possible
- ✅ Revoke unused keys - Clean up old keys immediately
- ❌ Never commit keys - Check .env files are .gitignored
- ❌ Never log keys - Avoid echo/print statements
Workflow Security
- ✅ Pin action versions - Use
@v4not@main - ✅ Review third-party actions - Check permissions
- ✅ Limit workflow permissions - Use minimum required
- ✅ Use
if: always()- Upload results even if tests fail
Next Steps
✅ Configuration complete! Now:
- Quick Start Guide - Complete end-to-end example
- View Dashboard - Monitor your tests
- API Reference - Advanced integration options