Skillshub customerio-deploy-pipeline
install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/jeremylongshore/claude-code-plugins-plus-skills/customerio-deploy-pipeline" ~/.claude/skills/comeonoliver-skillshub-customerio-deploy-pipeline && rm -rf "$T"
manifest:
skills/jeremylongshore/claude-code-plugins-plus-skills/customerio-deploy-pipeline/SKILL.mdsource content
Customer.io Deploy Pipeline
Overview
Deploy Customer.io integrations to production: GCP Cloud Run with Secret Manager, Vercel serverless functions, AWS Lambda with SSM, Kubernetes with external secrets, plus health check endpoints and blue-green deployment scripts.
Prerequisites
- CI/CD pipeline configured (see
)customerio-ci-integration - Cloud platform credentials and access
- Production Customer.io credentials in a secrets manager
Instructions
Step 1: Deploy to Google Cloud Run
# .github/workflows/deploy-cloud-run.yml name: Deploy to Cloud Run on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest permissions: contents: read id-token: write # Required for Workload Identity Federation steps: - uses: actions/checkout@v4 - id: auth uses: google-github-actions/auth@v2 with: workload_identity_provider: ${{ secrets.WIF_PROVIDER }} service_account: ${{ secrets.WIF_SA }} - uses: google-github-actions/setup-gcloud@v2 - name: Build and push run: | gcloud builds submit --tag gcr.io/${{ secrets.GCP_PROJECT }}/cio-service - name: Deploy run: | gcloud run deploy cio-service \ --image gcr.io/${{ secrets.GCP_PROJECT }}/cio-service \ --region us-central1 \ --set-secrets "CUSTOMERIO_SITE_ID=cio-site-id:latest,\ CUSTOMERIO_TRACK_API_KEY=cio-track-key:latest,\ CUSTOMERIO_APP_API_KEY=cio-app-key:latest" \ --set-env-vars "CUSTOMERIO_REGION=us,NODE_ENV=production" \ --min-instances 1 \ --max-instances 10 \ --memory 512Mi \ --cpu 1 \ --allow-unauthenticated
Step 2: Health Check Endpoint
// routes/health.ts import { TrackClient, RegionUS } from "customerio-node"; import { Router } from "express"; const router = Router(); router.get("/health", async (_req, res) => { const checks: Record<string, { status: string; latency_ms?: number }> = {}; // Check Track API const cio = new TrackClient( process.env.CUSTOMERIO_SITE_ID!, process.env.CUSTOMERIO_TRACK_API_KEY!, { region: RegionUS } ); const start = Date.now(); try { await cio.identify("health-check", { email: "health@internal.example.com", _health_check: true, }); checks.track_api = { status: "ok", latency_ms: Date.now() - start }; } catch (err: any) { checks.track_api = { status: `error: ${err.statusCode}` }; } const allOk = Object.values(checks).every((c) => c.status === "ok"); res.status(allOk ? 200 : 503).json({ status: allOk ? "healthy" : "degraded", checks, version: process.env.npm_package_version ?? "unknown", uptime_seconds: Math.floor(process.uptime()), timestamp: new Date().toISOString(), }); }); export default router;
Step 3: Vercel Serverless Functions
// api/customerio/identify.ts (Vercel serverless function) import type { VercelRequest, VercelResponse } from "@vercel/node"; import { TrackClient, RegionUS } from "customerio-node"; const cio = new TrackClient( process.env.CUSTOMERIO_SITE_ID!, process.env.CUSTOMERIO_TRACK_API_KEY!, { region: RegionUS } ); export default async function handler(req: VercelRequest, res: VercelResponse) { if (req.method !== "POST") { return res.status(405).json({ error: "Method not allowed" }); } const { userId, attributes } = req.body; if (!userId || !attributes?.email) { return res.status(400).json({ error: "userId and attributes.email required" }); } try { await cio.identify(userId, { ...attributes, last_seen_at: Math.floor(Date.now() / 1000), }); return res.status(200).json({ success: true }); } catch (err: any) { return res.status(err.statusCode ?? 500).json({ error: err.message }); } }
Step 4: Kubernetes Deployment
# k8s/customerio-service.yaml apiVersion: apps/v1 kind: Deployment metadata: name: customerio-service spec: replicas: 2 selector: matchLabels: app: customerio-service template: metadata: labels: app: customerio-service spec: containers: - name: app image: gcr.io/my-project/cio-service:latest ports: - containerPort: 3000 env: - name: CUSTOMERIO_SITE_ID valueFrom: secretKeyRef: name: customerio-secrets key: site-id - name: CUSTOMERIO_TRACK_API_KEY valueFrom: secretKeyRef: name: customerio-secrets key: track-api-key - name: CUSTOMERIO_APP_API_KEY valueFrom: secretKeyRef: name: customerio-secrets key: app-api-key - name: CUSTOMERIO_REGION value: "us" - name: NODE_ENV value: "production" resources: requests: cpu: 100m memory: 256Mi limits: cpu: 500m memory: 512Mi readinessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 15 periodSeconds: 30 --- apiVersion: v1 kind: Service metadata: name: customerio-service spec: selector: app: customerio-service ports: - port: 80 targetPort: 3000
Step 5: Blue-Green Deployment
#!/usr/bin/env bash set -euo pipefail # scripts/blue-green-deploy.sh SERVICE="cio-service" REGION="us-central1" IMAGE="gcr.io/${GCP_PROJECT}/${SERVICE}:${COMMIT_SHA}" echo "=== Blue-Green Deploy: ${SERVICE} ===" # 1. Deploy with no traffic gcloud run deploy "${SERVICE}" \ --image "${IMAGE}" \ --region "${REGION}" \ --no-traffic \ --tag "canary" echo "Deployed canary. Running health check..." # 2. Health check on canary CANARY_URL=$(gcloud run services describe "${SERVICE}" \ --region "${REGION}" --format 'value(status.url)' \ | sed 's|https://|https://canary---|') HEALTH=$(curl -s -o /dev/null -w "%{http_code}" "${CANARY_URL}/health") if [ "${HEALTH}" != "200" ]; then echo "FAIL: Health check returned ${HEALTH}. Aborting." exit 1 fi # 3. Shift traffic: 10% → 50% → 100% for pct in 10 50 100; do echo "Shifting ${pct}% traffic to canary..." gcloud run services update-traffic "${SERVICE}" \ --region "${REGION}" \ --to-tags "canary=${pct}" sleep 30 done echo "Deploy complete. 100% traffic on new revision."
Deployment Checklist
- Production secrets in secrets manager (not env files)
- Health check endpoint responds 200
- Readiness and liveness probes configured
- Resource limits set (CPU, memory)
- Min instances > 0 (avoid cold starts)
- Blue-green or canary deployment configured
- Rollback procedure documented
- Post-deploy smoke test automated
Error Handling
| Issue | Solution |
|---|---|
| Secret not found | Verify secret name in secrets manager |
| Health check timeout | Increase initialDelaySeconds, check CIO connectivity |
| Cold start latency | Set (Cloud Run) or keep-alive |
| Memory OOM | Increase memory limits, check for event queue buildup |
Resources
Next Steps
After deployment, proceed to
customerio-webhooks-events for webhook handling.