BambooHR CI Integration
Overview
Set up CI/CD pipelines for BambooHR integrations with proper secret management, unit tests with mocked API, and optional integration tests against the real BambooHR API.
Prerequisites
- GitHub repository with Actions enabled
- BambooHR test API key (sandbox company or test account)
- npm/pnpm project with test suite configured
Instructions
Step 1: Configure GitHub Secrets
gh secret set BAMBOOHR_API_KEY --body "your-test-api-key"
gh secret set BAMBOOHR_COMPANY_DOMAIN --body "your-test-company"
gh secret set BAMBOOHR_WEBHOOK_SECRET --body "your-webhook-hmac-secret"
Step 2: GitHub Actions Workflow
name: BambooHR Integration
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * 1-5'
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run typecheck
- name: Unit tests (mocked BambooHR API)
run: npm test -- --coverage --reporter=verbose
- uses: actions/upload-artifact@v4
if: always()
with:
name: coverage
path: coverage/
integration-tests:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
needs: unit-tests
env:
BAMBOOHR_API_KEY: ${{ secrets.BAMBOOHR_API_KEY }}
BAMBOOHR_COMPANY_DOMAIN: ${{ secrets.BAMBOOHR_COMPANY_DOMAIN }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Integration tests (real BambooHR API)
run: npm run test:integration
timeout-minutes: 5
- name: API health check
run: |
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-u "${BAMBOOHR_API_KEY}:x" \
-H "Accept: application/json" \
"https://api.bamboohr.com/api/gateway.php/${BAMBOOHR_COMPANY_DOMAIN}/v1/employees/directory")
echo "BambooHR API status: $STATUS"
[ "$STATUS" -eq 200 ] || exit 1
Step 3: Test Structure
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';
import { BambooHRClient } from '../../src/bamboohr/client';
const BASE = 'https://api.bamboohr.com/api/gateway.php/testco/v1';
const handlers = [
http.get(`${BASE}/employees/directory`, () =>
HttpResponse.json({
employees: [
{ id: '1', displayName: 'Jane', jobTitle: 'Eng', department: 'Dev' },
],
}),
),
http.get(`${BASE}/employees/:id/`, () =>
HttpResponse.json({ id: '1', firstName: 'Jane', lastName: 'Smith' }),
),
http.post(`${BASE}/reports/custom`, () =>
HttpResponse.({ : , : [] }),
),
http.(,
(, { : , : { : } }),
),
];
server = (...handlers);
( server.());
( server.());
( server.());
(, {
client = ({ : , : });
(, () => {
dir = client.();
(dir.).();
(dir.[].).();
});
(, () => {
emp = client.(, [, ]);
(emp.).();
});
(, () => {
report = client.([, ]);
(report.).();
});
(, () => {
(
client.(, ),
)..();
});
});
import { describe, it, expect } from 'vitest';
import { BambooHRClient } from '../../src/bamboohr/client';
const HAS_CREDS = !!process.env.BAMBOOHR_API_KEY && !!process.env.BAMBOOHR_COMPANY_DOMAIN;
describe.skipIf(!HAS_CREDS)('BambooHR Live API', () => {
const client = new BambooHRClient({
companyDomain: process.env.BAMBOOHR_COMPANY_DOMAIN!,
apiKey: process.env.BAMBOOHR_API_KEY!,
});
it('should fetch employee directory', async () => {
const dir = await client.getDirectory();
expect(dir.employees.length).toBeGreaterThan(0);
expect(dir.employees[0]).toHaveProperty('displayName');
expect(dir.employees[0]).toHaveProperty('jobTitle');
}, 15_000);
(, () => {
report = client.([, , ]);
(report).();
(.(report.)).();
}, );
(, () => {
types = client.(, );
(types).();
}, );
});
Step 4: PR Status Check
required_status_checks:
- 'unit-tests'
Step 5: Scheduled API Health Monitoring
name: BambooHR API Health
on:
schedule:
- cron: '0 */4 * * *'
jobs:
health-check:
runs-on: ubuntu-latest
env:
BAMBOOHR_API_KEY: ${{ secrets.BAMBOOHR_API_KEY }}
BAMBOOHR_COMPANY_DOMAIN: ${{ secrets.BAMBOOHR_COMPANY_DOMAIN }}
steps:
- name: Check BambooHR API
run: |
STATUS=$(curl -s -o /tmp/response.json -w "%{http_code}" \
-u "${BAMBOOHR_API_KEY}:x" \
-H "Accept: application/json" \
"https://api.bamboohr.com/api/gateway.php/${BAMBOOHR_COMPANY_DOMAIN}/v1/employees/directory")
if [ "$STATUS" -ne 200 ]; then
echo "::error::BambooHR API returned $STATUS"
exit 1
fi
COUNT=$(cat /tmp/response.json | jq '.employees | length')
echo
Output
- Unit test pipeline with mocked BambooHR API
- Integration test pipeline with real API (gated on secrets)
- Scheduled health monitoring workflow
- PR status checks configured
- Coverage reports uploaded
Error Handling
| Issue | Cause | Solution |
|---|
| Secret not available in PR | Fork PR (no secrets access) | Use if guard on integration job |
| Integration test timeout | BambooHR API slow | Set timeout-minutes: 5 |
| Flaky 503 in tests | Rate limiting in CI | Add retry logic to test helpers |
| Health check false alarm | BambooHR maintenance | Check status page before alerting |
Resources
Next Steps
For deployment patterns, see bamboohr-deploy-integration.