Files
wehub-resource-sync bb5c75ce05
Component Security Validation / Security Audit (push) Has been cancelled
Deploy to Cloudflare Pages / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:58 +08:00

665 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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<string, string>;
preview: Record<string, string>;
production: Record<string, string>;
}
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<string, string> {
// 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<string, string>,
remote: Record<string, string>
): 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<string, string>) {
const categories: Record<string, Array<{
key: string;
description?: string;
example?: string;
}>> = {
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.
<function_calls>
<invoke name="TodoWrite">
<parameter name="todos">[{"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"}]