CI/CD Integration
Comvi’s CDN serves translations at runtime, so CI rarely needs to bundle them. But CI is still where you want to run pre-flight checks: key coverage, ICU validation, and — if you use a build-time bundle or a staging-to-prod promotion — the actual sync step.
What CI actually needs to do
Section titled “What CI actually needs to do”| Step | Why | Frequency |
|---|---|---|
| Pull translations | Seed or refresh a local copy (only if you bundle at build time — not needed for runtime CDN reads) | Every build |
| Push new keys | Developer added checkout.new_banner; ensure it exists in Comvi | On PR merge |
| Run type generation | Keep Translations TS type in sync with keys | Every build |
| Promote staging → production | Publish approved drafts to the prod CDN | On release tag |
Most apps need only push new keys + type generation. The rest depend on your setup.
Recommended setup
Section titled “Recommended setup”-
Create a CI-specific API key
In API Keys, create a key named
github-actions-ci(or equivalent) with the minimum permissions your pipeline needs:- Read-only CI:
project:read,translations:read,schema:read - Push-new-keys CI: add
translations:write
Set an expiration (6–12 months). Rotate on schedule.
- Read-only CI:
-
Store the key as a CI secret
- GitHub: Settings → Secrets and variables → Actions →
COMVI_API_KEY - GitLab: Settings → CI/CD → Variables →
COMVI_API_KEY(mark as masked)
- GitHub: Settings → Secrets and variables → Actions →
-
Add a
.comvirc.jsonto your repo (committable; the key is in the env){"apiBaseUrl": "https://api.comvi.io","translationsPath": "./src/locales","fileTemplate": "{languageTag}/{namespace}.json","format": "json"} -
Wire the CLI into your workflow
See the examples below.
Example workflows
Section titled “Example workflows”Push new keys on merge to main
Section titled “Push new keys on merge to main”name: Sync translation keyson: push: branches: [main]
jobs: push-keys: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 24 } - run: npm ci - name: Extract and push keys run: npx @comvi/cli push --dry-run env: COMVI_API_KEY: ${{ secrets.COMVI_API_KEY }} - run: npx @comvi/cli push env: COMVI_API_KEY: ${{ secrets.COMVI_API_KEY }}push-keys: stage: post-merge only: - main script: - npm ci - npx @comvi/cli push variables: COMVI_API_KEY: $COMVI_API_KEYThe CLI scans your source code for t('...') calls, diffs against Comvi, and creates missing keys. Deletions are not automatic — run the CLI locally with --prune to clean up unused keys after a refactor.
Type generation before build
Section titled “Type generation before build”- name: Generate translation types run: npx @comvi/cli typegen env: COMVI_API_KEY: ${{ secrets.COMVI_API_KEY }}
- run: npm run buildThis pulls the current key set and writes Translations types into your source tree. Check the result into git or regenerate on every build — see CLI typegen.
Promote staging → production on release
Section titled “Promote staging → production on release”For the two-project pattern from Staging → Production:
name: Promote translations to productionon: release: types: [published]
jobs: promote: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 24 }
- name: Pull from staging run: npx comvi pull --path ./promote env: COMVI_API_KEY: ${{ secrets.COMVI_STAGING_KEY }}
- name: Push to production run: npx comvi push --path ./promote --force-mode keep env: COMVI_API_KEY: ${{ secrets.COMVI_PROD_KEY }}The API key determines the source or destination project. --force-mode keep avoids overwriting existing production translations during promotion.
Validation steps
Section titled “Validation steps”Coverage gate
Section titled “Coverage gate”Fail the build if any language drops below a threshold — e.g., “production languages must be ≥95% translated”. Query via the REST API:
curl -H "X-API-Key: $COMVI_API_KEY" \ "https://api.comvi.io/api/v1/projects/$PROJECT_ID/stats" \ | jq '.languages[] | select(.code == "de") | .translated_ratio'If the value is below your threshold, exit 1 in the CI script. This catches “we forgot to translate the new feature” before release.
ICU validation gate
Section titled “ICU validation gate”The editor already rejects broken ICU, but if you import translations from files, run comvi pull --validate in CI. A placeholder mismatch fails the build.
Webhooks vs CLI-in-CI
Section titled “Webhooks vs CLI-in-CI”Two ways to react to translation changes:
- Pull on build — simple, deterministic, works in offline CI. Downside: your CI doesn’t know when translations changed, so fresh copy only lands on the next scheduled build.
- Webhook-triggered build — set up a webhook on
translation.updated(see Webhooks) that triggers your CI viarepository_dispatch(GitHub) or a pipeline trigger. Fresh translations within minutes.
For runtime-CDN apps, neither is needed — the CDN is the source of truth.
Security checklist
Section titled “Security checklist”- API keys as secrets, never in committed config. The
${COMVI_API_KEY}substitution in config files resolves from env. - Separate keys per environment and per purpose. Don’t share the prod push key with the staging CI.
last usedin the dashboard should match your expected cadence. An unused CI key is a leak risk.- Rotate on contributor offboarding — a departing engineer may have pulled the CI secret into a local shell.
See API Keys for the full security guide.
Troubleshooting
Section titled “Troubleshooting”comvi push says “no new keys” but my new key isn’t in Comvi
Section titled “comvi push says “no new keys” but my new key isn’t in Comvi”The extractor didn’t find the call in your code. Check your CLI config’s extract.patterns — if you use a custom t wrapper, you may need to register it.
Pipeline hangs on comvi pull
Section titled “Pipeline hangs on comvi pull”Probably a 401/403 with no output. Run with --verbose and check the key’s scopes. The pull endpoint needs project:read and translations:read; type generation also needs schema:read.
Translations in CI differ from what translators see
Section titled “Translations in CI differ from what translators see”CI may be caching an old CDN response. The CDN cache is short (minutes), but aggressive CI caches can hold for longer. Clear the cache or add a cache-busting param.
Bundle size grew unexpectedly
Section titled “Bundle size grew unexpectedly”A new namespace got pulled by accident. Check namespaces in your config — it should list only the ones this app loads.