CI/CD Integration
Framedash CLI integrates with any CI/CD system. This guide covers Jenkins and TeamCity with ready-to-use examples.
Prerequisites
Section titled “Prerequisites”- Install the CLI in your CI environment:
npm install -g @framedash/cli- The performance gate examples use
jqandbcfor JSON parsing and threshold checks. Ensure they are available on your CI agent. - Create a Full preset API key from the project’s API Keys page on the Framedash dashboard. The CLI examples in this guide include raw SQL queries and map uploads, so they need
data:adminandresources:write. If your pipeline also runs the game SDK to emit telemetry, create a separate Ingest preset key for that stage (see CI Profiling). - Note your Project ID from the project page URL (
/projects/{projectId}).
Environment Variables
Section titled “Environment Variables”Configure these in your CI system’s secret management:
| Variable | Required | Description |
|---|---|---|
FRAMEDASH_API_KEY | Yes | API key with the scopes needed by the step. Use Full for these examples; use an Ingest key (events:write) only for game telemetry emission. |
FRAMEDASH_PROJECT_ID | Yes | Target project UUID |
FRAMEDASH_BASE_URL | No | Custom API host URL (default: https://app.framedash.dev) |
Jenkins
Section titled “Jenkins”Credentials Setup
Section titled “Credentials Setup”Store the API key as a Jenkins credential (Secret text):
- Go to Manage Jenkins > Credentials > System > Global credentials
- Add a Secret text credential with ID
framedash-api-key - Add a Secret text credential with ID
framedash-project-id
Declarative Pipeline
Section titled “Declarative Pipeline”Post-Build Telemetry Check
Section titled “Post-Build Telemetry Check”Verify that the game build is sending telemetry data after an automated test run:
pipeline { agent any
environment { FRAMEDASH_API_KEY = credentials('framedash-api-key') FRAMEDASH_PROJECT_ID = credentials('framedash-project-id') }
stages { stage('Build') { steps { sh 'your-game-build-command' } }
stage('Automated Test') { steps { sh 'your-automated-test-command' } }
stage('Verify Telemetry') { steps { sh ''' framedash auth framedash status --format json > status.json echo "Project status:" framedash status --format table ''' } } }}Performance Regression Gate
Section titled “Performance Regression Gate”Fail the build if performance metrics fall below thresholds:
pipeline { agent any
environment { FRAMEDASH_API_KEY = credentials('framedash-api-key') FRAMEDASH_PROJECT_ID = credentials('framedash-project-id') }
stages { stage('Build & Test') { steps { sh 'your-game-build-and-test-command' } }
stage('Performance Gate') { steps { sh ''' RESULT=$(framedash query \ "SELECT avg(fps) as avg_fps, \ avg(frame_time_ms) as avg_frame_time \ FROM events \ WHERE event_name = 'perf_heartbeat' \ AND timestamp > now() - INTERVAL 1 HOUR" \ --format json)
AVG_FPS=$(echo "$RESULT" | jq -r '.rows[0].avg_fps // 0')
echo "Average FPS: $AVG_FPS"
if [ "$(echo "$AVG_FPS < 30" | bc -l)" -eq 1 ]; then echo "FAILED: FPS below 30 threshold ($AVG_FPS)" exit 1 fi
echo "PASSED: FPS meets threshold" ''' } } }
post { failure { echo 'Performance regression detected. Check the Framedash dashboard for details.' } }}Map Capture Upload
Section titled “Map Capture Upload”Automatically upload map images after level builds:
pipeline { agent any
environment { FRAMEDASH_API_KEY = credentials('framedash-api-key') FRAMEDASH_PROJECT_ID = credentials('framedash-project-id') }
stages { stage('Build Level') { steps { sh 'your-level-build-command' } }
stage('Capture Maps') { steps { sh 'your-map-capture-command --output-dir ./map_captures' } }
stage('Upload Maps') { steps { sh ''' framedash map-capture \ --input-dir ./map_captures \ --upload ''' } } }}Content Registry Sync
Section titled “Content Registry Sync”Keep the content registry in sync with your game data:
pipeline { agent any
environment { FRAMEDASH_API_KEY = credentials('framedash-api-key') FRAMEDASH_PROJECT_ID = credentials('framedash-project-id') }
stages { stage('Sync Content') { steps { sh ''' framedash content import ./game-content.json echo "Content registry updated" framedash content list --format table ''' } } }}Shared Library
Section titled “Shared Library”For organizations with multiple game projects, create a Jenkins Shared Library:
def call(Map config = [:]) { def queryDays = config.get('days', 7)
withCredentials([ string(credentialsId: config.apiKeyCredentialId ?: 'framedash-api-key', variable: 'FRAMEDASH_API_KEY'), string(credentialsId: config.projectIdCredentialId ?: 'framedash-project-id', variable: 'FRAMEDASH_PROJECT_ID') ]) { sh """ echo "Framedash dashboard (${queryDays}-day summary):" framedash dashboard --days ${queryDays} --format table """ }}Usage in a Jenkinsfile:
@Library('your-shared-lib') _
pipeline { agent any stages { stage('Framedash Report') { steps { framedashReport(days: 7) } } }}TeamCity
Section titled “TeamCity”Parameter Setup
Section titled “Parameter Setup”Add these as Configuration Parameters in your build configuration:
- Go to Build Configuration > Parameters
- Add parameter
env.FRAMEDASH_API_KEY(type: Password, spec:password display='hidden') - Add parameter
env.FRAMEDASH_PROJECT_ID(type: Text)
Or define them at the Project level for shared use across build configurations.
Build Steps
Section titled “Build Steps”Post-Build Telemetry Check
Section titled “Post-Build Telemetry Check”Add a Command Line build step:
Step name: Verify Framedash Telemetry
#!/bin/bashset -euo pipefail
echo "Verifying Framedash authentication..."framedash auth
echo "Project status:"framedash status --format tablePerformance Regression Gate
Section titled “Performance Regression Gate”Add a Command Line build step:
Step name: Framedash Performance Gate
#!/bin/bashset -euo pipefail
echo "Querying performance metrics..."RESULT=$(framedash query \ "SELECT avg(fps) as avg_fps, \ avg(frame_time_ms) as avg_frame_time \ FROM events \ WHERE event_name = 'perf_heartbeat' \ AND timestamp > now() - INTERVAL 1 HOUR" \ --format json)
AVG_FPS=$(echo "$RESULT" | jq -r '.rows[0].avg_fps // 0')
echo "Average FPS: $AVG_FPS"
if [ "$(echo "$AVG_FPS < 30" | bc -l)" -eq 1 ]; then echo "##teamcity[buildProblem description='FPS below threshold: $AVG_FPS']" exit 1fi
echo "##teamcity[buildStatisticValue key='framedash.avgFps' value='$AVG_FPS']"echo "Performance gate passed"Map Capture Upload
Section titled “Map Capture Upload”Add a Command Line build step:
Step name: Upload Map Captures
#!/bin/bashset -euo pipefail
echo "Validating map captures..."framedash map-capture --input-dir ./map_captures --dry-run
echo "Uploading map captures..."framedash map-capture --input-dir ./map_captures --uploadFunnel Analysis Report
Section titled “Funnel Analysis Report”Add a Command Line build step to track conversion funnels:
#!/bin/bashset -euo pipefail
echo "Funnel analysis (7-day window):"framedash funnel \ --steps "tutorial_start,tutorial_complete,first_purchase" \ --days 7 \ --format tableBuild Configuration Template (Kotlin DSL)
Section titled “Build Configuration Template (Kotlin DSL)”For TeamCity projects using Kotlin DSL:
import jetbrains.buildServer.configs.kotlin.*import jetbrains.buildServer.configs.kotlin.buildSteps.script
object FramedashPerformanceGate : BuildType({ name = "Framedash Performance Gate"
params { password("env.FRAMEDASH_API_KEY", "", display = ParameterDisplay.HIDDEN) text("env.FRAMEDASH_PROJECT_ID", "") text("framedash.minFps", "30", label = "Minimum FPS Threshold") }
steps { script { name = "Install Framedash CLI" scriptContent = "npm install -g @framedash/cli" } script { name = "Performance Gate" scriptContent = """ #!/bin/bash set -euo pipefail
MIN_FPS=%framedash.minFps%
RESULT=${'$'}(framedash query \ "SELECT avg(fps) as avg_fps FROM events \ WHERE event_name = 'perf_heartbeat' \ AND timestamp > now() - INTERVAL 1 HOUR" \ --format json)
AVG_FPS=${'$'}(echo "${'$'}RESULT" | jq -r '.rows[0].avg_fps // 0')
echo "Average FPS: ${'$'}AVG_FPS (threshold: ${'$'}MIN_FPS)"
if [ "${'$'}(echo "${'$'}AVG_FPS < ${'$'}MIN_FPS" | bc -l)" -eq 1 ]; then echo "##teamcity[buildProblem description='FPS below threshold']" exit 1 fi
echo "##teamcity[buildStatisticValue key='framedash.avgFps' value='${'$'}AVG_FPS']" """.trimIndent() } }})Meta-Runner
Section titled “Meta-Runner”Create a reusable meta-runner for Framedash checks. Save as Framedash_Performance_Gate.xml in your project’s .teamcity/pluginData/metaRunners/ directory:
<?xml version="1.0" encoding="UTF-8"?><meta-runner name="Framedash Performance Gate"> <description>Check game performance metrics via Framedash CLI</description> <settings> <parameters> <param name="framedash.minFps" value="30" spec="text description='Minimum acceptable FPS' label='Min FPS'" /> <param name="framedash.queryDays" value="7" spec="text description='Number of days to query' label='Query Days'" /> </parameters> <build-runners> <runner name="Framedash Performance Gate" type="simpleRunner"> <parameters> <param name="script.content"><![CDATA[#!/bin/bashset -euo pipefail
framedash auth
MIN_FPS=%framedash.minFps%
RESULT=$(framedash query \ "SELECT avg(fps) as avg_fps FROM events \ WHERE event_name = 'perf_heartbeat' \ AND timestamp > now() - INTERVAL %framedash.queryDays% DAY" \ --format json)
AVG_FPS=$(echo "$RESULT" | jq -r '.rows[0].avg_fps // 0')
echo "Average FPS: $AVG_FPS (threshold: $MIN_FPS)"
if [ "$(echo "$AVG_FPS < $MIN_FPS" | bc -l)" -eq 1 ]; then echo "##teamcity[buildProblem description='FPS below threshold: $AVG_FPS < $MIN_FPS']" exit 1fi
echo "##teamcity[buildStatisticValue key='framedash.avgFps' value='$AVG_FPS']"echo "Performance gate passed"]]></param> <param name="teamcity.step.mode" value="default" /> <param name="use.custom.script" value="true" /> </parameters> </runner> </build-runners> </settings></meta-runner>Common Patterns
Section titled “Common Patterns”SQL-Based Quality Gates
Section titled “SQL-Based Quality Gates”Use framedash query with SQL files for repeatable checks:
-- checks/fps-threshold.sqlSELECT avg(fps) as avg_fps, min(fps) as min_fps, quantile(0.05)(fps) as p5_fpsFROM eventsWHERE event_name = 'perf_heartbeat' AND timestamp > now() - INTERVAL 1 DAYframedash query --file checks/fps-threshold.sql --format jsonOutput Format for Parsing
Section titled “Output Format for Parsing”Use --format json with jq for reliable CI parsing:
# Extract a single valueframedash dashboard --days 7 --format json | jq '.kpis.events'
# Check project statusframedash status --format json | jq '.project.name'The CLI already unwraps the API response envelope, so read top-level fields (.kpis, .project) directly rather than .data.*. The dashboard KPI member is events, not totalEvents.
Use --format table for human-readable build logs:
framedash dashboard --days 7 --format tableframedash retention --days 30 --format tableNightly Performance Report
Section titled “Nightly Performance Report”Schedule a nightly build that generates a performance summary:
#!/bin/bashset -euo pipefail
echo "=== Framedash Nightly Report ==="echo ""echo "--- Project Status ---"framedash status --format tableecho ""echo "--- 7-Day Dashboard ---"framedash dashboard --days 7 --format tableecho ""echo "--- Player Retention ---"framedash retention --days 30 --format tableecho ""echo "--- Alert Rules ---"framedash alerts list --format tableTroubleshooting
Section titled “Troubleshooting”Authentication Errors
Section titled “Authentication Errors”Error: Unauthorized (401)- Verify
FRAMEDASH_API_KEYis set and valid - Run
framedash authto test the key - Map uploads require the
resources:writescope. Use the Read & Write or Full preset.
Rate Limiting
Section titled “Rate Limiting”Error: Too Many Requests (429)- The rate limit is enforced per account (tenant) and shared across every project and API key the account owns, so parallel CI jobs draw from the same hourly budget (100 requests/hour on the free plan; higher plans differ)
- Honor the
Retry-Afterheader on every 429 rather than scheduling aroundX-RateLimit-Reset. The reset timestamp is derived from a sliding window and can understate the real wait right after the advertised reset - Consider upgrading your plan for higher limits
Network / Proxy
Section titled “Network / Proxy”If your CI environment uses a proxy:
export HTTPS_PROXY=http://proxy.example.com:8080framedash authCLI Not Found
Section titled “CLI Not Found”Ensure the CLI is installed in the CI agent’s PATH:
# Verify installationwhich framedash || npm install -g @framedash/cli
# Or use npx without global installnpx @framedash/cli authNext Steps
Section titled “Next Steps”- CLI Reference: Full command documentation
- API Reference: REST API details
- CI Profiling: Automated performance profiling setup