CI/CD Integration
Integrate EdgeGate into your GitHub Actions workflow to automatically test AI model performance on Qualcomm Snapdragon hardware. Set up in 5 minutes.
Quick Start
Get hardware regression testing running on your PRs in 4 steps.
Get Your Credentials
- Log in to your EdgeGate Dashboard
- Open your Workspace → Settings → Integrations
- Click Generate CI Secret and copy the secret
- Note your Workspace ID from the URL or Settings page
Add GitHub Secrets
In your repository, go to Settings → Secrets and variables → Actions and add:
| Secret | Description |
|---|---|
| EDGEGATE_WORKSPACE_ID | Your workspace UUID |
| EDGEGATE_API_SECRET | The CI secret you generated |
| EDGEGATE_PIPELINE_ID | (Optional) Pipeline UUID to run |
| EDGEGATE_MODEL_ARTIFACT_ID | (Optional) Model artifact UUID to test |
Create Workflow File
Create a file at .github/workflows/edgegate.yml in your repository and paste the following content:
name: EdgeGate AI Test
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
contents: read
pull-requests: write
issues: write
checks: write
jobs:
edgegate-test:
name: EdgeGate AI Performance Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run EdgeGate Test
uses: actions/github-script@v7
env:
WORKSPACE_ID: ${{ secrets.EDGEGATE_WORKSPACE_ID }}
API_SECRET: ${{ secrets.EDGEGATE_API_SECRET }}
PIPELINE_ID: ${{ secrets.EDGEGATE_PIPELINE_ID }}
MODEL_ARTIFACT_ID: ${{ secrets.EDGEGATE_MODEL_ARTIFACT_ID }}
EDGEGATE_API_URL: https://edgegateapi.frozo.ai
with:
script: |
const crypto = require('crypto');
const timestamp = new Date().toISOString();
const nonce = crypto.randomUUID();
const body = JSON.stringify({
pipeline_id: process.env.PIPELINE_ID,
model_artifact_id: process.env.MODEL_ARTIFACT_ID,
commit_sha: context.sha,
branch: context.ref,
pull_request: context.payload.pull_request?.number
});
const message = timestamp + '\n' + nonce + '\n' + body;
const signature = crypto
.createHmac('sha256', process.env.API_SECRET)
.update(message)
.digest('hex');
const response = await fetch(
process.env.EDGEGATE_API_URL + '/v1/ci/github/run',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-EdgeGate-Workspace': process.env.WORKSPACE_ID,
'X-EdgeGate-Timestamp': timestamp,
'X-EdgeGate-Nonce': nonce,
'X-EdgeGate-Signature': signature,
},
body: body,
}
);
const result = await response.json();
console.log('EdgeGate response:', JSON.stringify(result, null, 2));
if (!response.ok) {
core.setFailed('EdgeGate test failed: ' + result.detail);
} else {
core.info('Run queued: ' + result.run_id);
}Note: For the full production-ready script, please refer to our Integration Guide.
Create a PR
The workflow runs automatically on every PR to main. You'll see:
- ✓ Authentication verification
- bar_chart Performance test results (inference time, peak memory)
- verified Pass/fail gate status on your PR check
Matrix Mode in CI
Run multiple models against multiple devices in one test — and optionally swap in a freshly-built model from CI on every PR.
EdgeGate ships a composite GitHub Action that handles three CI patterns out of the box. Drop one block into your workflow — no JWT, no JSON-by-hand, no bash gymnastics.
hubPattern A — Static matrix
Pipeline is configured once (via dashboard / API) with model_matrix. CI just triggers — server fans out across all M × D cells. Omit model-artifact-id entirely.
name: EdgeGate AI Test
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/edgegate
with:
workspace-id: ${{ secrets.EDGEGATE_WORKSPACE_ID }}
api-secret: ${{ secrets.EDGEGATE_API_SECRET }}
pipeline-id: ${{ secrets.EDGEGATE_PIPELINE_ID }}
# model-artifact-id intentionally omitted — matrix is on the pipelinebuildPattern B — Dynamic regression (CI uploads fresh model + baseline matrix)
CI builds a new ONNX model on every PR and you want to regression-test it against a set of known-good baseline models. The action uploads the freshly-built file, swaps it into the pipeline matrix alongside your baselines, then triggers the run — all in one step.
name: EdgeGate Regression Test
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build model
run: python scripts/build_model.py --out build/model.onnx
- uses: ./.github/actions/edgegate
with:
workspace-id: ${{ secrets.EDGEGATE_WORKSPACE_ID }}
api-secret: ${{ secrets.EDGEGATE_API_SECRET }}
pipeline-id: ${{ secrets.EDGEGATE_PIPELINE_ID }}
model-path: build/model.onnx
baseline-artifact-ids: ${{ secrets.EDGEGATE_BASELINE_IDS }}
model-label: ${{ github.sha }}
# ↑ Action does: upload model.onnx → POST /v1/ci/github/upload-artifact
# replace matrix → POST /v1/ci/github/pipelines/{id}/matrix
# (fresh + baseline1 + baseline2 ...)
# trigger run → POST /v1/ci/github/runHow matrix swap works
The action replaces the pipeline's model_matrix wholesale. After the run, the matrix on the pipeline reflects what CI tested most recently. To keep a stable baseline set, store the baseline artifact UUIDs in the EDGEGATE_BASELINE_IDS secret (comma-separated). Hard limit: M × D ≤ 25 cells, M ≤ 10 models per matrix.
shieldAuth model
Pattern B uses three CI endpoints, all HMAC-authenticated with the same EDGEGATE_API_SECRET you already use for runs. No long-lived JWT, no separate upload key.
POST /v1/ci/github/upload-artifact— signature coverstimestamp\nnonce\nsha256(file). Server re-hashes the upload and refuses on mismatch.POST /v1/ci/github/pipelines/{id}/matrix— standard HMAC; replacesmodel_matrix; pipeline gates / devices / promptpack untouched.POST /v1/ci/github/run— standard HMAC; matrix-mode triggers omitmodel_artifact_id.
Full Integration
For complete on-device AI performance testing on every PR.
Create a Pipeline
- Go to Pipelines → Create Pipeline
- Select target devices (e.g., Snapdragon 8 Gen 3, Snapdragon 7s Gen 2)
- Define quality gates:
gates:
- metric: inference_time_ms
operator: lte
threshold: 1.0 # Inference ≤ 1.0ms
- metric: peak_memory_mb
operator: lte
threshold: 150.0 # Peak memory ≤ 150MBCopy the Pipeline ID from the pipeline detail page.
Upload Your Model
- Go to Artifacts → Upload Model
- Upload your ONNX, TorchScript, or TFLite model
- Copy the Model Artifact ID
Add Pipeline Secrets
Add these additional secrets to your GitHub repository:
| Secret | Value |
|---|---|
| EDGEGATE_PIPELINE_ID | Pipeline UUID from step 1 |
| EDGEGATE_MODEL_ARTIFACT_ID | (Optional) Model Artifact UUID. Leave unset for matrix pipelines — the model set lives on the pipeline. |
Now every PR will trigger full on-device AI regression tests automatically.
HuggingFace Token (Optional)
Connect a personal HuggingFace token so EdgeGate can import private, gated, or Qualcomm-org repos when uploading models.
By default, model imports from HuggingFace use anonymous access — fine for public repos. To pull models behind a license click-through (gated) or from a private / org-scoped repository, connect a personal token in workspace settings. The token is validated against HuggingFace whoami before being encrypted at rest with envelope encryption (same KMS as the AI Hub token).
Generate a HuggingFace token
- Visit huggingface.co/settings/tokens
- Click Create new token — Read scope is sufficient.
- Copy the token (starts with
hf_).
Connect it to your workspace
- Open your workspace → Settings → HuggingFace.
- Paste the token and click Connect HuggingFace.
- You'll see your connected account name and type (user / organization).
Requires Admin role on the workspace. The endpoints exposed (all under /v1/workspaces/{ws}/integrations/huggingface):
| Method | Path | Action |
|---|---|---|
| POST | / | Connect a token (validates first) |
| GET | / | Get integration status |
| PUT | /rotate | Replace the stored token |
| PUT | /disable | Pause without deleting |
| PUT | /enable | Re-enable a paused integration |
| DELETE | / | Permanently remove |
Tip: the same flows are exposed as MCP tools (edgegate_connect_huggingface, edgegate_get_huggingface_integration, edgegate_disconnect_huggingface) — see /docs/mcp.
API Reference
All CI requests use HMAC-SHA256 authentication for security.
Authentication Headers
| Header | Description |
|---|---|
| X-EdgeGate-Workspace | Workspace UUID |
| X-EdgeGate-Timestamp | ISO 8601 timestamp (e.g. 2026-01-11T12:00:00Z) |
| X-EdgeGate-Nonce | Unique request ID (UUID v4) |
| X-EdgeGate-Signature | HMAC-SHA256 signature |
Signature Computation
The signature is an HMAC-SHA256 digest of the message string using your CI secret as the key:
# Message format: timestamp + newline + nonce + newline + body
# For POST requests (with body):
MESSAGE=$(printf '%s\n%s\n%s' "$TIMESTAMP" "$NONCE" "$BODY")
SIGNATURE=$(printf '%s' "$MESSAGE" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
# For GET requests (no body):
MESSAGE=$(printf '%s\n%s\n' "$TIMESTAMP" "$NONCE")
SIGNATURE=$(printf '%s' "$MESSAGE" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')Endpoints
/v1/ci/statusTest CI authentication. Returns 200 if your credentials are valid.
Response (200)
{
"status": "ok",
"workspace_id": "uuid",
"message": "CI authentication successful"
}/v1/ci/github/runTrigger a performance test run on real Snapdragon hardware.
Request Body — single-model pipeline
{
"pipeline_id": "uuid",
"model_artifact_id": "uuid",
"commit_sha": "abc123",
"branch": "feature/new-model",
"pull_request": 42
}Request Body — matrix-mode pipeline
Omit model_artifact_id entirely. The matrix is declared on the pipeline (via dashboard or PUT /v1/pipelines) and CI just triggers — server fans out across all M × D cells.
{
"pipeline_id": "uuid",
"commit_sha": "abc123",
"branch": "feature/new-model",
"pull_request": 42
}Response (202)
{
"run_id": "uuid",
"status": "queued",
"pipeline_id": "uuid",
"message": "Run queued successfully"
}/v1/ci/runs/{run_id}Poll the status of a run. Use this after triggering a run to wait for completion and check pass/fail results.
Response (200)
{
"run_id": "uuid",
"status": "passed",
"pipeline_id": "uuid",
"gates_passed": 2,
"gates_total": 2,
"metrics": {
"inference_time_ms": 0.176,
"peak_memory_mb": 121.51
},
"error": null,
"completed_at": "2026-02-25T12:00:00Z"
}Possible status values: queued running passed failed error
Troubleshooting
error"Invalid signature" error
- Verify
EDGEGATE_API_SECRETis correctly set (43 characters) - Check timestamp is current (±5 minutes tolerance)
- Ensure message format:
timestamp\nnonce\nbody
error"Nonce already used" error
Each nonce can only be used once. The workflow generates unique UUIDs automatically. If you're testing manually, generate a new UUID for each request.
errorPipeline/Model not found
Verify the UUIDs are correct and belong to your workspace. Check the pipeline and artifact pages in the dashboard to confirm.
Ready to get started?
Create your free account and add hardware regression testing to your CI/CD pipeline in minutes.