--- allowed-tools: Read, Write, Edit, Bash argument-hint: [--pull] [--push] [--validate] [--backup] description: Synchronize environment variables between local development and Vercel deployments --- ## Vercel Environment Sync **Sync Operation**: $ARGUMENTS ## Current Environment Analysis ### Local Environment - Environment files: - @.env.local (if exists) - @.env.development (if exists) - @.env.production (if exists) - @.env (if exists) - Environment example: @.env.example (if exists) - Vercel config: @vercel.json (if exists) ### Project Status - Vercel CLI status: !`vercel --version 2>/dev/null || echo "Vercel CLI not installed"` - Current project: !`vercel project ls 2>/dev/null | head -5 || echo "Not linked to Vercel project"` - Git status: !`git status --porcelain | head -5` ## Environment Synchronization Strategy ### 1. Environment File Analysis ```typescript // Environment file structure analysis interface EnvironmentConfig { development: Record; preview: Record; production: Record; } const environmentFiles = { '.env.local': 'Local development overrides', '.env.development': 'Development environment', '.env.staging': 'Staging/preview environment', '.env.production': 'Production environment', '.env': 'Default environment (committed to git)', '.env.example': 'Environment template (safe to commit)', }; ``` ### 2. Vercel Environment Management ```bash # List all environment variables for all environments vercel env ls # List environment variables for specific environment vercel env ls --environment=production vercel env ls --environment=preview vercel env ls --environment=development # Pull environment variables from Vercel vercel env pull .env.vercel # Add new environment variable vercel env add [name] [environment] # Remove environment variable vercel env rm [name] [environment] ``` ## Synchronization Operations ### 1. Pull Environment Variables from Vercel ```bash #!/bin/bash # Pull environments from Vercel echo "🔄 Pulling environment variables from Vercel..." # Create backup of existing files if [ -f .env.local ]; then cp .env.local .env.local.backup.$(date +%Y%m%d_%H%M%S) echo "📦 Backup created for .env.local" fi # Pull from Vercel (creates .env.local by default) vercel env pull .env.local if [ $? -eq 0 ]; then echo "✅ Successfully pulled environment variables" echo "📁 Variables saved to .env.local" # Show summary echo "" echo "📊 Environment Variables Summary:" echo "================================" grep -c "=" .env.local 2>/dev/null && echo "Total variables: $(grep -c "=" .env.local)" # List variable names (hide values for security) echo "" echo "🔑 Variable Names:" grep "^[A-Z]" .env.local | cut -d'=' -f1 | sort else echo "❌ Failed to pull environment variables" exit 1 fi ``` ### 2. Push Environment Variables to Vercel ```bash #!/bin/bash # Push environment variables to Vercel echo "🚀 Pushing environment variables to Vercel..." # Check if environment files exist ENV_FILES=(".env.production" ".env.staging" ".env.development") FOUND_FILES=() for file in "${ENV_FILES[@]}"; do if [ -f "$file" ]; then FOUND_FILES+=("$file") fi done if [ ${#FOUND_FILES[@]} -eq 0 ]; then echo "❌ No environment files found to push" echo "💡 Expected files: ${ENV_FILES[*]}" exit 1 fi # Push each environment file for file in "${FOUND_FILES[@]}"; do echo "📤 Processing $file..." # Determine target environment if [[ "$file" == *"production"* ]]; then ENV="production" elif [[ "$file" == *"staging"* ]]; then ENV="preview" # Vercel uses 'preview' for staging elif [[ "$file" == *"development"* ]]; then ENV="development" else ENV="development" # Default fi echo "🎯 Pushing to $ENV environment..." # Read variables from file and push to Vercel while IFS='=' read -r key value; do # Skip empty lines and comments if [[ -z "$key" || "$key" =~ ^#.* ]]; then continue fi # Remove quotes from value if present value=$(echo "$value" | sed 's/^"\(.*\)"$/\1/' | sed "s/^'\(.*\)'$/\1/") echo " 🔑 Setting $key..." echo "$value" | vercel env add "$key" "$ENV" --force done < "$file" echo "✅ Completed $file -> $ENV" echo "" done echo "🎉 All environment variables pushed successfully!" ``` ### 3. Environment Validation ```typescript // Environment validation script interface ValidationRule { name: string; required: boolean; pattern?: RegExp; description: string; } const validationRules: ValidationRule[] = [ { name: 'DATABASE_URL', required: true, pattern: /^(postgresql|mysql|sqlite):\/\/.+/, description: 'Database connection string', }, { name: 'NEXTAUTH_SECRET', required: true, pattern: /.{32,}/, description: 'NextAuth.js secret key (min 32 characters)', }, { name: 'NEXTAUTH_URL', required: true, pattern: /^https?:\/\/.+/, description: 'NextAuth.js canonical URL', }, { name: 'API_KEY', required: false, pattern: /^[A-Za-z0-9_-]+$/, description: 'API key for external services', }, ]; function validateEnvironment(envFile: string): ValidationResult { const errors: string[] = []; const warnings: string[] = []; const env = readEnvironmentFile(envFile); // Check required variables validationRules.forEach(rule => { const value = env[rule.name]; if (rule.required && !value) { errors.push(`Missing required variable: ${rule.name}`); return; } if (value && rule.pattern && !rule.pattern.test(value)) { errors.push(`Invalid format for ${rule.name}: ${rule.description}`); } }); // Check for common issues Object.entries(env).forEach(([key, value]) => { // Check for placeholder values if (value === 'your-secret-here' || value === 'change-me') { warnings.push(`Placeholder value detected for ${key}`); } // Check for potentially committed secrets if (key.includes('SECRET') || key.includes('PRIVATE')) { if (value.length < 16) { warnings.push(`${key} appears to be too short for a secret`); } } }); return { valid: errors.length === 0, errors, warnings, }; } function readEnvironmentFile(filePath: string): Record { // Implementation to read and parse environment file return {}; } interface ValidationResult { valid: boolean; errors: string[]; warnings: string[]; } ``` ### 4. Environment Backup and Restore ```bash #!/bin/bash # Backup and restore environment variables BACKUP_DIR=".env-backups" TIMESTAMP=$(date +%Y%m%d_%H%M%S) backup_environment() { echo "📦 Creating environment backup..." mkdir -p "$BACKUP_DIR" # Backup local files for file in .env.local .env.development .env.staging .env.production; do if [ -f "$file" ]; then cp "$file" "$BACKUP_DIR/${file}.${TIMESTAMP}" echo "✅ Backed up $file" fi done # Backup Vercel environment variables echo "📤 Backing up Vercel environment variables..." for env in production preview development; do vercel env ls --environment="$env" > "$BACKUP_DIR/vercel-${env}.${TIMESTAMP}.txt" echo "✅ Backed up Vercel $env environment" done echo "🎉 Backup completed in $BACKUP_DIR/" ls -la "$BACKUP_DIR/" | grep "$TIMESTAMP" } restore_environment() { local backup_timestamp="$1" if [ -z "$backup_timestamp" ]; then echo "❌ Please specify backup timestamp" echo "💡 Available backups:" ls -1 "$BACKUP_DIR/" | grep -E "\.env" | cut -d'.' -f3 | sort -u exit 1 fi echo "🔄 Restoring environment from backup $backup_timestamp..." # Restore local files for file in .env.local .env.development .env.staging .env.production; do backup_file="$BACKUP_DIR/${file}.${backup_timestamp}" if [ -f "$backup_file" ]; then cp "$backup_file" "$file" echo "✅ Restored $file" fi done echo "🎉 Environment restored from backup" } # Usage functions case "$1" in backup) backup_environment ;; restore) restore_environment "$2" ;; *) echo "Usage: $0 {backup|restore} [timestamp]" exit 1 ;; esac ``` ## Advanced Synchronization Features ### 1. Environment Diff and Comparison ```typescript // Environment comparison tool interface EnvironmentDiff { added: string[]; removed: string[]; modified: Array<{ key: string; local: string; remote: string; }>; unchanged: string[]; } function compareEnvironments( local: Record, remote: Record ): EnvironmentDiff { const diff: EnvironmentDiff = { added: [], removed: [], modified: [], unchanged: [], }; const allKeys = new Set([...Object.keys(local), ...Object.keys(remote)]); allKeys.forEach(key => { if (!(key in local)) { diff.added.push(key); } else if (!(key in remote)) { diff.removed.push(key); } else if (local[key] !== remote[key]) { diff.modified.push({ key, local: local[key], remote: remote[key], }); } else { diff.unchanged.push(key); } }); return diff; } // Generate diff report function generateDiffReport(diff: EnvironmentDiff): string { let report = '# Environment Variables Comparison\n\n'; if (diff.added.length > 0) { report += '## ➕ Variables in Remote (not in Local)\n'; diff.added.forEach(key => { report += `- \`${key}\`\n`; }); report += '\n'; } if (diff.removed.length > 0) { report += '## ➖ Variables in Local (not in Remote)\n'; diff.removed.forEach(key => { report += `- \`${key}\`\n`; }); report += '\n'; } if (diff.modified.length > 0) { report += '## 🔄 Modified Variables\n'; diff.modified.forEach(({ key, local, remote }) => { report += `### \`${key}\`\n`; report += `- **Local**: \`${maskSensitive(local)}\`\n`; report += `- **Remote**: \`${maskSensitive(remote)}\`\n\n`; }); } if (diff.unchanged.length > 0) { report += `## ✅ Unchanged Variables (${diff.unchanged.length})\n`; report += `${diff.unchanged.map(key => `- \`${key}\``).join('\n')}\n\n`; } return report; } function maskSensitive(value: string): string { // Mask sensitive values for security if (value.length <= 8) { return '*'.repeat(value.length); } return `${value.substring(0, 4)}${'*'.repeat(value.length - 8)}${value.substring(value.length - 4)}`; } ``` ### 2. Environment Template Generation ```typescript // Generate .env.example from existing environment function generateEnvExample(envFile: string): string { const env = readEnvironmentFile(envFile); let template = '# Environment Variables Template\n'; template += '# Copy this file to .env.local and fill in the values\n\n'; const categories = categorizeVariables(env); Object.entries(categories).forEach(([category, variables]) => { template += `# ${category.toUpperCase()}\n`; variables.forEach(({ key, description, example }) => { if (description) { template += `# ${description}\n`; } template += `${key}=${example || 'your-value-here'}\n\n`; }); }); return template; } function categorizeVariables(env: Record) { const categories: Record> = { database: [], authentication: [], external_apis: [], configuration: [], }; Object.keys(env).forEach(key => { if (key.includes('DATABASE') || key.includes('DB_')) { categories.database.push({ key, description: getDatabaseDescription(key) }); } else if (key.includes('AUTH') || key.includes('SECRET')) { categories.authentication.push({ key, description: getAuthDescription(key) }); } else if (key.includes('API_KEY') || key.includes('_TOKEN')) { categories.external_apis.push({ key, description: getApiDescription(key) }); } else { categories.configuration.push({ key, description: getConfigDescription(key) }); } }); return categories; } function getDatabaseDescription(key: string): string { if (key === 'DATABASE_URL') return 'Database connection string'; if (key === 'DB_HOST') return 'Database host'; if (key === 'DB_PORT') return 'Database port'; if (key === 'DB_NAME') return 'Database name'; return 'Database configuration'; } function getAuthDescription(key: string): string { if (key === 'NEXTAUTH_SECRET') return 'NextAuth.js secret key'; if (key === 'NEXTAUTH_URL') return 'NextAuth.js canonical URL'; if (key === 'JWT_SECRET') return 'JWT secret key'; return 'Authentication configuration'; } function getApiDescription(key: string): string { return `API key for ${key.toLowerCase().replace(/_/g, ' ')}`; } function getConfigDescription(key: string): string { return `Configuration for ${key.toLowerCase().replace(/_/g, ' ')}`; } ``` ### 3. Security and Validation ```bash #!/bin/bash # Security checks for environment variables security_check() { echo "🔐 Running security checks on environment variables..." local issues=0 # Check for common security issues for file in .env.local .env.development .env.staging .env.production; do if [ ! -f "$file" ]; then continue fi echo "🔍 Checking $file..." # Check for weak secrets while IFS='=' read -r key value; do if [[ -z "$key" || "$key" =~ ^#.* ]]; then continue fi # Remove quotes value=$(echo "$value" | sed 's/^"\(.*\)"$/\1/' | sed "s/^'\(.*\)'$/\1/") # Check for placeholder values if [[ "$value" == *"your-"* || "$value" == *"change-me"* || "$value" == *"replace-me"* ]]; then echo "⚠️ Placeholder value in $key" ((issues++)) fi # Check for short secrets if [[ "$key" =~ (SECRET|PRIVATE|KEY|TOKEN) ]]; then if [ ${#value} -lt 16 ]; then echo "⚠️ $key appears to be too short for a secret (${#value} characters)" ((issues++)) fi fi # Check for hardcoded URLs in production if [[ "$file" == *"production"* && "$value" =~ localhost ]]; then echo "⚠️ $key contains localhost in production environment" ((issues++)) fi done < "$file" done # Check if .env files are in .gitignore if [ -f .gitignore ]; then if ! grep -q ".env.local" .gitignore; then echo "⚠️ .env.local not in .gitignore" ((issues++)) fi if ! grep -q ".env.production" .gitignore; then echo "⚠️ .env.production not in .gitignore" ((issues++)) fi else echo "⚠️ No .gitignore file found" ((issues++)) fi echo "" if [ $issues -eq 0 ]; then echo "✅ No security issues found" else echo "❌ Found $issues security issues" exit 1 fi } security_check ``` ## Automation and Integration ### 1. GitHub Actions Integration ```yaml # .github/workflows/env-sync.yml name: Environment Sync on: push: branches: [main, develop] paths: ['.env.example', '.env.*'] workflow_dispatch: inputs: action: description: 'Sync action' required: true default: 'validate' type: choice options: - validate - pull - push jobs: env-sync: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Vercel CLI run: npm i -g vercel@latest - name: Link to Vercel run: vercel link --token=${{ secrets.VERCEL_TOKEN }} --yes - name: Validate Environment if: github.event.inputs.action == 'validate' || github.event.inputs.action == '' run: | # Run environment validation node scripts/validate-env.js - name: Pull Environment if: github.event.inputs.action == 'pull' run: | vercel env pull .env.ci --token=${{ secrets.VERCEL_TOKEN }} # Validate pulled environment node scripts/validate-env.js .env.ci - name: Push Environment if: github.event.inputs.action == 'push' run: | # Push environment variables to Vercel node scripts/push-env.js --token=${{ secrets.VERCEL_TOKEN }} ``` ### 2. Development Workflow Integration ```bash #!/bin/bash # Pre-commit hook for environment validation echo "🔍 Validating environment variables..." # Check if environment files are properly configured if [ -f .env.local ]; then echo "✅ .env.local exists" else echo "⚠️ .env.local not found" if [ -f .env.example ]; then echo "💡 Copy .env.example to .env.local and configure your variables" fi fi # Validate environment structure node scripts/validate-env.js if [ $? -ne 0 ]; then echo "❌ Environment validation failed" exit 1 fi echo "✅ Environment validation passed" ``` ## Comprehensive Sync Operations Implement complete environment synchronization workflow with validation, backup, security checks, and automation integration for seamless development and deployment processes. [{"content": "Analyze hook system and context monitoring capabilities", "status": "completed"}, {"content": "Design context threshold detection mechanism", "status": "completed"}, {"content": "Create hook script for automatic compact + clear sequence", "status": "pending"}, {"content": "Configure hook in settings.json with proper event triggers", "status": "pending"}, {"content": "Test hook execution and threshold detection", "status": "pending"}, {"content": "Add markdown copy button to Supabase blog post", "status": "completed"}, {"content": "Write Claude Code + Next.js and Vercel Integration article", "status": "completed"}, {"content": "Create Next.js Architecture Expert agent", "status": "completed"}, {"content": "Create Vercel Deployment Specialist agent", "status": "completed"}, {"content": "Create React Performance Optimizer agent", "status": "completed"}, {"content": "Create Next.js app scaffolding command", "status": "completed"}, {"content": "Create Vercel deployment optimization command", "status": "completed"}, {"content": "Create component generator command", "status": "completed"}, {"content": "Create API route tester command", "status": "completed"}, {"content": "Create bundle analyzer command", "status": "completed"}, {"content": "Create middleware creator command", "status": "completed"}, {"content": "Create edge function generator command", "status": "completed"}, {"content": "Create performance audit command", "status": "completed"}, {"content": "Create environment sync command", "status": "completed"}, {"content": "Create migration helper command", "status": "in_progress"}]