Claude-code-plugins-plus-skills lindy-ci-integration
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/lindy-pack/skills/lindy-ci-integration" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-lindy-ci-integration && rm -rf "$T"
manifest:
plugins/saas-packs/lindy-pack/skills/lindy-ci-integration/SKILL.mdsource content
Lindy CI Integration
Overview
Lindy agents run on Lindy's managed platform — CI/CD tests your integration code: webhook receivers, callback handlers, and application logic that interacts with Lindy agents. Test webhook signature verification, payload processing, and error handling without hitting live Lindy endpoints.
Prerequisites
- GitHub repository with Actions enabled
- Lindy API key and webhook secret stored as GitHub secrets
- Node.js project with webhook receiver code
- Completed
setuplindy-install-auth
Instructions
Step 1: Store Secrets in GitHub
gh secret set LINDY_API_KEY --body "lnd_live_xxxxxxxxxxxx" gh secret set LINDY_WEBHOOK_SECRET --body "whsec_xxxxxxxxxxxx"
Step 2: Create GitHub Actions Workflow
# .github/workflows/lindy-integration.yml name: Lindy Integration Tests on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - run: npm ci - name: Run unit tests run: npm test env: LINDY_WEBHOOK_SECRET: ${{ secrets.LINDY_WEBHOOK_SECRET }} - name: Validate webhook handler run: npm run test:webhook - name: Connectivity check (non-blocking) continue-on-error: true run: | curl -s -o /dev/null -w "%{http_code}" \ -X POST "https://public.lindy.ai/api/v1/webhooks/health" \ -H "Authorization: Bearer ${{ secrets.LINDY_API_KEY }}"
Step 3: Write Webhook Handler Tests
// __tests__/webhook-handler.test.ts import { describe, it, expect, vi } from 'vitest'; import request from 'supertest'; import { app } from '../src/server'; describe('Lindy Webhook Handler', () => { const VALID_SECRET = process.env.LINDY_WEBHOOK_SECRET || 'test-secret'; it('rejects requests without auth header', async () => { const res = await request(app) .post('/lindy/callback') .send({ event: 'test' }); expect(res.status).toBe(401); }); it('rejects requests with wrong auth token', async () => { const res = await request(app) .post('/lindy/callback') .set('Authorization', 'Bearer wrong-token') .send({ event: 'test' }); expect(res.status).toBe(401); }); it('accepts requests with valid auth token', async () => { const res = await request(app) .post('/lindy/callback') .set('Authorization', `Bearer ${VALID_SECRET}`) .set('Content-Type', 'application/json') .send({ taskId: 'task_123', status: 'completed', result: { summary: 'Test result' }, }); expect(res.status).toBe(200); expect(res.body.received).toBe(true); }); it('handles malformed payload gracefully', async () => { const res = await request(app) .post('/lindy/callback') .set('Authorization', `Bearer ${VALID_SECRET}`) .set('Content-Type', 'application/json') .send('not-json'); expect(res.status).toBeLessThan(500); }); it('processes webhook payload fields correctly', async () => { const payload = { taskId: 'task_456', status: 'completed', result: { classification: 'billing', sentiment: 'neutral', summary: 'Customer asks about invoice #789', }, }; const res = await request(app) .post('/lindy/callback') .set('Authorization', `Bearer ${VALID_SECRET}`) .set('Content-Type', 'application/json') .send(payload); expect(res.status).toBe(200); }); });
Step 4: Add Smoke Test for Live Connectivity
// __tests__/connectivity.test.ts import { describe, it, expect } from 'vitest'; describe('Lindy Connectivity (smoke)', () => { it('can reach Lindy webhook endpoint', async () => { const webhookUrl = process.env.LINDY_WEBHOOK_URL; if (!webhookUrl) { console.warn('LINDY_WEBHOOK_URL not set, skipping connectivity test'); return; } const response = await fetch(webhookUrl, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.LINDY_WEBHOOK_SECRET}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ test: true, ci: true }), }); expect(response.ok).toBe(true); }); });
Step 5: PR Status Check
# Add to existing workflow - name: Post test results if: always() uses: actions/github-script@v7 with: script: | const status = '${{ job.status }}' === 'success' ? 'Passed' : 'Failed'; if (context.payload.pull_request) { github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.pull_request.number, body: `Lindy Integration Tests: **${status}**` }); }
Test Categories
| Category | What It Tests | Requires Live Lindy? |
|---|---|---|
| Auth verification | Webhook signature checking | No |
| Payload processing | Data extraction and transformation | No |
| Error handling | Malformed input, edge cases | No |
| Connectivity | Webhook endpoint reachability | Yes (non-blocking) |
| End-to-end | Full trigger -> callback cycle | Yes |
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Secret not found in CI | Not configured | |
| Connectivity test fails | Lindy endpoint unreachable in CI | Mark as |
| Tests timeout | Network latency | Set on job |
| Flaky live tests | Lindy processing delay | Add retry logic or mock external calls |
Resources
Next Steps
Proceed to
lindy-deploy-integration for deployment automation.