Claude-skill-registry Knack DevOps
Manages automated build, deployment, environment sync, and monitoring for HTI's Knack-Vercel integration. Ensures dashboard uptime, data sync relia...
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/knack-devops" ~/.claude/skills/majiayu000-claude-skill-registry-knack-devops && rm -rf "$T"
manifest:
skills/data/knack-devops/SKILL.mdsource content
Knack DevOps
Purpose
Manages automated build, deployment, environment sync, and monitoring for HTI's Knack-Vercel integration. Ensures dashboard uptime, data sync reliability, and performance optimization.
Core Functions
trigger_vercel_build
Purpose: Programmatically deploy updated dashboard code
Parameters:
(string, optional): Deployment URL (default: production)vercel_url
(string, optional): "push" | "webhook" | "api" (default: "api")trigger
Example:
const deployment = await trigger_vercel_build({ vercel_url: process.env.VERCEL_PROJECT_URL, trigger: "api" }); // Output: // { // deployment_id: "dpl_abc123", // status: "building", // url: "https://hubdash-git-main.vercel.app", // estimated_completion: "2025-04-15T14:35:00Z" // }
Implementation:
import { Vercel } from '@vercel/sdk'; async function trigger_vercel_build({ vercel_url }) { const vercel = new Vercel({ bearerToken: process.env.VERCEL_TOKEN }); const deployment = await vercel.deployments.create({ name: 'hubdash', gitSource: { type: 'github', ref: 'main' } }); return { deployment_id: deployment.id, status: deployment.status, url: deployment.url }; }
monitor_latency
Purpose: Track API and dashboard response times
Parameters:
(string, required): Endpoint to monitorapi_url
(integer, optional): Check frequency (default: 300 = 5 min)interval_seconds
(integer, optional): Latency threshold for alerts (default: 2000)alert_threshold_ms
Example:
const monitor = await monitor_latency({ api_url: "https://hubdash.vercel.app/api/metrics", interval_seconds: 300, alert_threshold_ms: 2000 });
Implementation:
import axios from 'axios'; async function monitor_latency({ api_url, interval_seconds, alert_threshold_ms }) { setInterval(async () => { const start = Date.now(); try { await axios.get(api_url); const latency = Date.now() - start; console.log(`Latency: ${latency}ms`); if (latency > alert_threshold_ms) { await alert_team({ severity: "warning", message: `Dashboard latency high: ${latency}ms (threshold: ${alert_threshold_ms}ms)` }); } // Log to metrics system await log_metric({ metric: "api_latency", value: latency, timestamp: new Date() }); } catch (error) { await alert_team({ severity: "critical", message: `Dashboard unreachable: ${api_url}`, error: error.message }); } }, interval_seconds * 1000); }
sync_environment
Purpose: Keep Knack credentials and config in sync across environments
Environments:
- Development: Local testing with sandbox Knack instance
- Staging: Pre-production testing with production data copy
- Production: Live HTI dashboard with real grant data
Example:
await sync_environment({ source: "production", target: "staging", include: ["knack_credentials", "api_endpoints"], exclude: ["user_tokens", "sensitive_keys"] });
Implementation:
async function sync_environment({ source, target, include, exclude }) { // Fetch config from source const source_config = await get_env_config(source); // Filter based on include/exclude const filtered_config = Object.keys(source_config) .filter(key => include.includes(key) && !exclude.includes(key)) .reduce((obj, key) => { obj[key] = source_config[key]; return obj; }, {}); // Apply to target environment await update_env_config(target, filtered_config); return { synced_keys: Object.keys(filtered_config), source: source, target: target }; }
automated_backup
Purpose: Regular backups of Knack data for disaster recovery
Example:
// Daily backup of all Knack objects cron.schedule('0 2 * * *', async () => { const backup = await automated_backup({ objects: ["object_1", "object_2", "object_training"], destination: "s3://hti-backups/knack", retention_days: 90 }); console.log(`Backup complete: ${backup.records_backed_up} records`); });
Implementation:
import AWS from 'aws-sdk'; async function automated_backup({ objects, destination, retention_days }) { const s3 = new AWS.S3(); const timestamp = new Date().toISOString().split('T')[0]; let total_records = 0; for (const object_key of objects) { // Fetch all records const records = await fetch_all_pages(object_key); total_records += records.length; // Save to S3 await s3.putObject({ Bucket: 'hti-backups', Key: `knack/${object_key}_${timestamp}.json`, Body: JSON.stringify(records, null, 2), ContentType: 'application/json' }).promise(); } // Clean up old backups (>90 days) await cleanup_old_backups({ bucket: 'hti-backups', retention_days }); return { records_backed_up: total_records, timestamp }; }
CI/CD Pipeline
GitHub Actions Workflow
# .github/workflows/deploy.yml name: Deploy HTI Dashboard on: push: branches: [main] workflow_dispatch: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm ci - name: Run tests run: npm test - name: Validate Knack connection run: npm run test:knack-api env: KNACK_APP_ID: ${{ secrets.KNACK_APP_ID }} KNACK_API_KEY: ${{ secrets.KNACK_API_KEY }} deploy: needs: test runs-on: ubuntu-latest steps: - name: Deploy to Vercel uses: amondnet/vercel-action@v25 with: vercel-token: ${{ secrets.VERCEL_TOKEN }} vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} vercel-args: '--prod' - name: Notify deployment run: | curl -X POST https://hooks.slack.com/services/${{ secrets.SLACK_WEBHOOK }} \ -d '{"text": "✅ HTI Dashboard deployed to production"}'
Pre-Deployment Checks
async function pre_deployment_checks() { const checks = { knack_api: await test_knack_connection(), data_integrity: await validate_production_data(), cache_warm: await warm_cache(), env_vars: await verify_env_vars(), build_size: await check_bundle_size() }; const all_passed = Object.values(checks).every(c => c.passed); if (!all_passed) { throw new Error(`Pre-deployment checks failed: ${JSON.stringify(checks)}`); } return checks; } // Run before every production deploy async function test_knack_connection() { try { const response = await get_records("object_1", { rows_per_page: 1 }); return { passed: true, message: "Knack API accessible" }; } catch (error) { return { passed: false, message: `Knack API error: ${error.message}` }; } }
Monitoring & Alerting
Uptime Monitoring
// Use Vercel Analytics or external service (Pingdom, UptimeRobot) async function setup_uptime_monitoring() { // Vercel Analytics (automatic) // https://vercel.com/docs/analytics // Or external monitoring const monitor = await createUptimeCheck({ url: "https://hubdash.vercel.app", interval: 300, // 5 minutes alert_contacts: ["admin@hubzonetech.org"] }); return monitor; }
Error Tracking
// Sentry integration import * as Sentry from "@sentry/nextjs"; Sentry.init({ dsn: process.env.SENTRY_DSN, environment: process.env.NODE_ENV, tracesSampleRate: 1.0, beforeSend(event) { // Don't send test data errors if (event.request?.url?.includes('localhost')) { return null; } return event; } }); // Log Knack API errors try { await get_records("object_1"); } catch (error) { Sentry.captureException(error, { tags: { service: "knack_api" }, extra: { object_key: "object_1" } }); }
Performance Monitoring
// Web Vitals tracking import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'; function send_to_analytics(metric) { const body = JSON.stringify({ name: metric.name, value: metric.value, id: metric.id }); fetch('/api/analytics', { method: 'POST', body }); } getCLS(send_to_analytics); getFID(send_to_analytics); getFCP(send_to_analytics); getLCP(send_to_analytics); getTTFB(send_to_analytics);
Environment Management
Development Environment
# .env.development KNACK_APP_ID=dev_app_id KNACK_API_KEY=dev_api_key VERCEL_ENV=development CACHE_TTL=30 # Shorter cache for testing DEBUG_MODE=true
Production Environment
# .env.production (in Vercel dashboard) KNACK_APP_ID=prod_app_id KNACK_API_KEY=prod_api_key VERCEL_ENV=production CACHE_TTL=300 # 5 minutes DEBUG_MODE=false SENTRY_DSN=https://...
Secrets Management
// Store secrets in Vercel Environment Variables // Never commit secrets to Git // Access in code: const knack_api_key = process.env.KNACK_API_KEY; // Rotate secrets quarterly (align with grant reporting) async function rotate_secrets() { // 1. Generate new API key in Knack dashboard // 2. Update Vercel environment variables // 3. Trigger redeployment // 4. Verify new keys work // 5. Revoke old keys }
Performance Optimization
Bundle Size Analysis
// Run during CI/CD import { readFileSync } from 'fs'; async function check_bundle_size() { const bundle_stats = JSON.parse(readFileSync('.next/analyze/client.json')); const total_size = bundle_stats.reduce((sum, bundle) => sum + bundle.size, 0); const max_size = 1_000_000; // 1MB if (total_size > max_size) { console.warn(`Bundle size ${total_size} exceeds ${max_size}`); return { passed: false, size: total_size }; } return { passed: true, size: total_size }; }
Cache Warming
// Warm cache after deployment async function warm_cache() { const critical_endpoints = [ "/api/metrics", "/api/county-breakdown", "/api/quarterly-report" ]; for (const endpoint of critical_endpoints) { await fetch(`https://hubdash.vercel.app${endpoint}`); } return { warmed: critical_endpoints.length }; }
Disaster Recovery
Database Recovery
async function restore_from_backup({ backup_date, objects }) { const s3 = new AWS.S3(); for (const object_key of objects) { // Fetch backup const backup = await s3.getObject({ Bucket: 'hti-backups', Key: `knack/${object_key}_${backup_date}.json` }).promise(); const records = JSON.parse(backup.Body.toString()); // Restore to Knack (batch insert) console.log(`Restoring ${records.length} records to ${object_key}`); // Implementation depends on Knack's batch API } }
Rollback Procedure
// Rollback to previous Vercel deployment async function rollback_deployment() { const vercel = new Vercel({ bearerToken: process.env.VERCEL_TOKEN }); // Get previous deployment const deployments = await vercel.deployments.list({ projectId: process.env.VERCEL_PROJECT_ID, limit: 2 }); const previous = deployments[1]; // Promote to production await vercel.deployments.update(previous.id, { target: 'production' }); console.log(`Rolled back to ${previous.id}`); }
Integration Points
- knack_reader: Monitor API health
- knack_cache_optimizer: Cache invalidation on deploy
- knack_realtime: Webhook endpoint health checks
- knack_reporting_sync: Automated report generation triggers
- knack_dashboard_ai: Performance metrics collection
Best Practices
- Automate everything: No manual deployments
- Test before deploy: CI/CD checks gate production
- Monitor continuously: Uptime + performance + errors
- Backup daily: 90-day retention
- Document runbooks: Incident response procedures
- Rotate secrets quarterly: Align with grant reporting cycle
Incident Response
Runbook: Dashboard Down
- Check Vercel status page
- Verify Knack API connectivity
- Review recent deployments
- Check error logs in Sentry
- If needed, rollback deployment
- Notify stakeholders
- Post-incident review
Runbook: Slow Dashboard
- Check API latency metrics
- Review cache hit rates
- Analyze bundle size
- Check Knack rate limits
- Optimize queries if needed
- Consider CDN configuration