Skip to main content
AI/MLjeremylongshore

deepgram-ci-integration

'Configure Deepgram CI/CD integration for automated testing and deployment.

Stars
2,267
Source
jeremylongshore/claude-code-plugins-plus-skills
Updated
2026-05-31
Slug
jeremylongshore--claude-code-plugins-plus-skills--deepgram-ci-integration
View on GitHubRaw SKILL.md

// install — copy + paste into any project

mkdir -p .claude/skills && curl -fsSL https://raw.githubusercontent.com/jeremylongshore/claude-code-plugins-plus-skills/HEAD/plugins/saas-packs/deepgram-pack/skills/deepgram-ci-integration/SKILL.md -o .claude/skills/deepgram-ci-integration.md

Drops the SKILL.md into .claude/skills/deepgram-ci-integration.md. Works with Claude Code, Cursor, and any agent that loads SKILL.md files from .claude/skills/.

Deepgram CI Integration

Overview

Set up CI/CD pipelines for Deepgram integrations with GitHub Actions. Includes unit tests with mocked SDK, integration tests against the real API, smoke tests, automated key rotation, and deployment gates.

Prerequisites

  • GitHub repository with Actions enabled
  • DEEPGRAM_API_KEY stored as repository secret
  • @deepgram/sdk and vitest installed
  • Test fixtures committed (or downloaded in CI)

Instructions

Step 1: GitHub Actions Workflow

# .github/workflows/deepgram-ci.yml
name: Deepgram CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  NODE_VERSION: '20'

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: npm
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck
      - run: npm test -- --reporter=verbose
        # Unit tests use mocked SDK — no API key needed

  integration-tests:
    runs-on: ubuntu-latest
    needs: unit-tests
    if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: npm
      - run: npm ci
      - run: npm run test:integration
        env:
          DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }}
        timeout-minutes: 5

  smoke-test:
    runs-on: ubuntu-latest
    needs: integration-tests
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: npm
      - run: npm ci && npm run build
      - name: Smoke test
        run: npx tsx scripts/smoke-test.ts
        env:
          DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }}
        timeout-minutes: 2

Step 2: Integration Test Suite

// tests/integration/deepgram.test.ts
import { describe, it, expect, beforeAll } from 'vitest';
import { createClient, DeepgramClient } from '@deepgram/sdk';

const SAMPLE_URL = 'https://static.deepgram.com/examples/Bueller-Life-moves-702702706.wav';

describe('Deepgram Integration', () => {
  let client: DeepgramClient;

  beforeAll(() => {
    const key = process.env.DEEPGRAM_API_KEY;
    if (!key) throw new Error('DEEPGRAM_API_KEY required for integration tests');
    client = createClient(key);
  });

  it('authenticates successfully', async () => {
    const { result, error } = await client.manage.getProjects();
    expect(error).toBeNull();
    expect(result.projects.length).toBeGreaterThan(0);
  });

  it('transcribes pre-recorded audio with Nova-3', async () => {
    const { result, error } = await client.listen.prerecorded.transcribeUrl(
      { url: SAMPLE_URL },
      { model: 'nova-3', smart_format: true }
    );
    expect(error).toBeNull();
    const alt = result.results.channels[0].alternatives[0];
    expect(alt.transcript).toContain('Life');
    expect(alt.confidence).toBeGreaterThan(0.85);
  }, 30000);

  it('returns word-level timing', async () => {
    const { result } = await client.listen.prerecorded.transcribeUrl(
      { url: SAMPLE_URL },
      { model: 'nova-3' }
    );
    const words = result.results.channels[0].alternatives[0].words;
    expect(words).toBeDefined();
    expect(words!.length).toBeGreaterThan(0);
    expect(words![0]).toHaveProperty('start');
    expect(words![0]).toHaveProperty('end');
    expect(words![0]).toHaveProperty('confidence');
  }, 30000);

  it('speaker diarization identifies speakers', async () => {
    const { result } = await client.listen.prerecorded.transcribeUrl(
      { url: SAMPLE_URL },
      { model: 'nova-3', diarize: true }
    );
    const words = result.results.channels[0].alternatives[0].words;
    expect(words?.some((w: any) => w.speaker !== undefined)).toBe(true);
  }, 30000);

  it('TTS generates audio stream', async () => {
    const response = await client.speak.request(
      { text: 'CI test.' },
      { model: 'aura-2-thalia-en', encoding: 'linear16', container: 'wav' }
    );
    const stream = await response.getStream();
    expect(stream).toBeTruthy();
  }, 15000);
});

Step 3: Smoke Test Script

// scripts/smoke-test.ts
import { createClient } from '@deepgram/sdk';

const SAMPLE_URL = 'https://static.deepgram.com/examples/Bueller-Life-moves-702702706.wav';

async function smokeTest() {
  console.log('Deepgram Smoke Test');
  console.log('='.repeat(40));

  const client = createClient(process.env.DEEPGRAM_API_KEY!);
  let passed = 0;
  let failed = 0;

  // Test 1: Authentication
  try {
    const { error } = await client.manage.getProjects();
    if (error) throw error;
    console.log('[PASS] Authentication');
    passed++;
  } catch (err: any) {
    console.error(`[FAIL] Authentication: ${err.message}`);
    failed++;
  }

  // Test 2: Pre-recorded transcription
  try {
    const { result, error } = await client.listen.prerecorded.transcribeUrl(
      { url: SAMPLE_URL },
      { model: 'nova-3', smart_format: true }
    );
    if (error) throw error;
    if (!result.results.channels[0].alternatives[0].transcript) {
      throw new Error('Empty transcript');
    }
    console.log('[PASS] Pre-recorded transcription');
    passed++;
  } catch (err: any) {
    console.error(`[FAIL] Pre-recorded transcription: ${err.message}`);
    failed++;
  }

  // Test 3: TTS
  try {
    const response = await client.speak.request(
      { text: 'Smoke test.' },
      { model: 'aura-2-thalia-en' }
    );
    const stream = await response.getStream();
    if (!stream) throw new Error('No audio stream');
    console.log('[PASS] Text-to-speech');
    passed++;
  } catch (err: any) {
    console.error(`[FAIL] Text-to-speech: ${err.message}`);
    failed++;
  }

  console.log(`\nResults: ${passed} passed, ${failed} failed`);
  process.exit(failed > 0 ? 1 : 0);
}

smokeTest();

Step 4: Package.json Scripts

{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest --watch",
    "test:integration": "vitest run tests/integration/",
    "test:smoke": "tsx scripts/smoke-test.ts",
    "lint": "eslint src/ tests/",
    "typecheck": "tsc --noEmit"
  }
}

Step 5: Vitest Configuration

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    include: ['tests/**/*.test.ts'],
    exclude: ['tests/integration/**'],  // Integration tests run separately
    coverage: {
      include: ['src/**'],
      reporter: ['text', 'lcov'],
    },
  },
});

Step 6: Automated Key Rotation (Scheduled)

# .github/workflows/rotate-deepgram-key.yml
name: Rotate Deepgram API Key

on:
  schedule:
    - cron: '0 0 1 */3 *'  # Quarterly (1st of every 3rd month)
  workflow_dispatch:

jobs:
  rotate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - name: Rotate key
        run: |
          NEW_KEY=$(npx tsx scripts/rotate-key.ts)
          gh secret set DEEPGRAM_API_KEY --body "$NEW_KEY"
          echo "Key rotated successfully"
        env:
          DEEPGRAM_ADMIN_KEY: ${{ secrets.DEEPGRAM_ADMIN_KEY }}
          DEEPGRAM_PROJECT_ID: ${{ secrets.DEEPGRAM_PROJECT_ID }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Output

  • GitHub Actions workflow (unit -> integration -> smoke)
  • Integration test suite covering STT, diarization, TTS
  • Smoke test script with pass/fail exit codes
  • Vitest configuration with integration test separation
  • Quarterly key rotation workflow

Error Handling

Issue Cause Resolution
Integration tests 401 Secret not set or expired Rotate DEEPGRAM_API_KEY secret
Smoke test timeout API latency Increase timeout-minutes
Tests pass locally, fail in CI Missing env var Check secrets are set in repo settings
Fork PRs can't access secrets GitHub security Skip integration tests on fork PRs

Resources