evernote-multi-env-setup
Configure multi-environment setup for Evernote integrations. Use when setting up dev, staging, and production environments, or managing environment-specific configurations. Trigger with phrases like "evernote environments", "evernote staging", "evernote dev setup", "multiple environments evernote". allowed-tools: Read, Write, Edit, Bash(npm:*), Grep version: 1.0.0 license: MIT author: Jeremy Longshore <jeremy@intentsolutions.io>
Allowed Tools
No tools specified
Provided by Plugin
evernote-pack
Claude Code skill pack for Evernote (24 skills)
Installation
This skill is included in the evernote-pack plugin:
/plugin install evernote-pack@claude-code-plugins-plus
Click to copy
Instructions
# Evernote Multi-Environment Setup
## Overview
Configure separate development, staging, and production environments for Evernote integrations with proper isolation and configuration management.
## Prerequisites
- Multiple Evernote API keys (sandbox and production)
- Environment management infrastructure
- CI/CD pipeline
## Environment Strategy
| Environment | Evernote Mode | Purpose | Users |
|-------------|---------------|---------|-------|
| Development | Sandbox | Local development | Developers |
| Staging | Sandbox | Integration testing | QA team |
| Production | Production | Live users | Customers |
## Instructions
### Step 1: Environment Configuration Files
```bash
# Project structure
project/
โโโ config/
โ โโโ default.js # Shared defaults
โ โโโ development.js # Dev overrides
โ โโโ staging.js # Staging overrides
โ โโโ production.js # Production overrides
โโโ .env.development
โโโ .env.staging
โโโ .env.production
โโโ .env.example
```
```javascript
// config/default.js
module.exports = {
evernote: {
requestTimeout: 30000,
connectionTimeout: 10000,
maxRetries: 3
},
cache: {
ttl: 300,
prefix: 'evernote:'
},
logging: {
redactTokens: true
}
};
```
```javascript
// config/development.js
module.exports = {
evernote: {
sandbox: true,
serviceHost: 'sandbox.evernote.com',
// More lenient for debugging
requestTimeout: 60000,
maxRetries: 1
},
cache: {
ttl: 60, // Short TTL for development
prefix: 'evernote:dev:'
},
logging: {
level: 'debug'
}
};
```
```javascript
// config/staging.js
module.exports = {
evernote: {
sandbox: true, // Still using sandbox for staging
serviceHost: 'sandbox.evernote.com',
requestTimeout: 30000,
maxRetries: 2
},
cache: {
ttl: 180,
prefix: 'evernote:staging:'
},
logging: {
level: 'info'
}
};
```
```javascript
// config/production.js
module.exports = {
evernote: {
sandbox: false, // Production mode
serviceHost: 'www.evernote.com',
requestTimeout: 30000,
maxRetries: 3
},
cache: {
ttl: 300,
prefix: 'evernote:prod:'
},
logging: {
level: 'warn'
}
};
```
### Step 2: Environment Variables
```bash
# .env.example
NODE_ENV=development
# Evernote
EVERNOTE_CONSUMER_KEY=
EVERNOTE_CONSUMER_SECRET=
EVERNOTE_SANDBOX=true
# App
APP_URL=http://localhost:3000
SESSION_SECRET=change-this-secret
# Database
DATABASE_URL=
# Redis
REDIS_URL=
```
```bash
# .env.development
NODE_ENV=development
EVERNOTE_CONSUMER_KEY=your-sandbox-key
EVERNOTE_CONSUMER_SECRET=your-sandbox-secret
EVERNOTE_SANDBOX=true
APP_URL=http://localhost:3000
SESSION_SECRET=dev-secret-not-secure
DATABASE_URL=postgresql://localhost:5432/evernote_dev
REDIS_URL=redis://localhost:6379/0
```
```bash
# .env.staging
NODE_ENV=staging
EVERNOTE_CONSUMER_KEY=your-sandbox-key
EVERNOTE_CONSUMER_SECRET=your-sandbox-secret
EVERNOTE_SANDBOX=true
APP_URL=https://staging.yourapp.com
# Secret managed externally (Vault, AWS Secrets, etc.)
# DATABASE_URL managed externally
# REDIS_URL managed externally
```
```bash
# .env.production
NODE_ENV=production
# ALL SECRETS MANAGED EXTERNALLY
EVERNOTE_SANDBOX=false
APP_URL=https://yourapp.com
```
### Step 3: Configuration Loader
```javascript
// config/index.js
const path = require('path');
const deepMerge = require('lodash.merge');
class ConfigLoader {
constructor() {
this.env = process.env.NODE_ENV || 'development';
this.config = this.loadConfig();
}
loadConfig() {
// Load default config
const defaultConfig = require('./default');
// Load environment-specific config
let envConfig = {};
try {
envConfig = require(`./${this.env}`);
} catch (error) {
console.warn(`No config file for environment: ${this.env}`);
}
// Merge configs
const merged = deepMerge({}, defaultConfig, envConfig);
// Override with environment variables
return this.applyEnvOverrides(merged);
}
applyEnvOverrides(config) {
// Evernote overrides
if (process.env.EVERNOTE_CONSUMER_KEY) {
config.evernote.consumerKey = process.env.EVERNOTE_CONSUMER_KEY;
}
if (process.env.EVERNOTE_CONSUMER_SECRET) {
config.evernote.consumerSecret = process.env.EVERNOTE_CONSUMER_SECRET;
}
if (process.env.EVERNOTE_SANDBOX !== undefined) {
config.evernote.sandbox = process.env.EVERNOTE_SANDBOX === 'true';
}
// App overrides
if (process.env.APP_URL) {
config.app = config.app || {};
config.app.url = process.env.APP_URL;
}
// Database overrides
if (process.env.DATABASE_URL) {
config.database = config.database || {};
config.database.url = process.env.DATABASE_URL;
}
// Redis overrides
if (process.env.REDIS_URL) {
config.redis = config.redis || {};
config.redis.url = process.env.REDIS_URL;
}
return config;
}
get(key) {
const keys = key.split('.');
let value = this.config;
for (const k of keys) {
if (value === undefined) return undefined;
value = value[k];
}
return value;
}
getAll() {
return this.config;
}
isProduction() {
return this.env === 'production';
}
isDevelopment() {
return this.env === 'development';
}
isStaging() {
return this.env === 'staging';
}
}
module.exports = new ConfigLoader();
```
### Step 4: Environment-Aware Client Factory
```javascript
// services/evernote-factory.js
const Evernote = require('evernote');
const config = require('../config');
class EvernoteClientFactory {
static createOAuthClient() {
return new Evernote.Client({
consumerKey: config.get('evernote.consumerKey'),
consumerSecret: config.get('evernote.consumerSecret'),
sandbox: config.get('evernote.sandbox'),
china: false
});
}
static createAuthenticatedClient(accessToken) {
return new Evernote.Client({
token: accessToken,
sandbox: config.get('evernote.sandbox')
});
}
static getEnvironmentInfo() {
return {
environment: process.env.NODE_ENV,
sandbox: config.get('evernote.sandbox'),
serviceHost: config.get('evernote.sandbox')
? 'sandbox.evernote.com'
: 'www.evernote.com'
};
}
static validateEnvironment() {
const errors = [];
if (!config.get('evernote.consumerKey')) {
errors.push('Missing EVERNOTE_CONSUMER_KEY');
}
if (!config.get('evernote.consumerSecret')) {
errors.push('Missing EVERNOTE_CONSUMER_SECRET');
}
if (config.isProduction() && config.get('evernote.sandbox')) {
errors.push('Production environment cannot use sandbox mode');
}
return {
valid: errors.length === 0,
errors
};
}
}
module.exports = EvernoteClientFactory;
```
### Step 5: Docker Compose for Local Development
```yaml
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/app
- /app/node_modules
ports:
- "3000:3000"
environment:
- NODE_ENV=development
env_file:
- .env.development
depends_on:
- postgres
- redis
postgres:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: evernote_dev
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
```
### Step 6: Environment-Specific Middleware
```javascript
// middleware/environment.js
const config = require('../config');
function environmentMiddleware(req, res, next) {
// Add environment info to request
req.environment = {
name: process.env.NODE_ENV,
sandbox: config.get('evernote.sandbox'),
isProduction: config.isProduction()
};
// Development-only features
if (config.isDevelopment()) {
// Add debug headers
res.setHeader('X-Environment', 'development');
res.setHeader('X-Evernote-Mode', 'sandbox');
}
// Production safety checks
if (config.isProduction()) {
// Ensure HTTPS
if (req.protocol !== 'https') {
return res.redirect(301, `https://${req.hostname}${req.url}`);
}
}
next();
}
function requireProduction(req, res, next) {
if (!config.isProduction()) {
return res.status(403).json({
error: 'This endpoint is only available in production'
});
}
next();
}
function blockInProduction(req, res, next) {
if (config.isProduction()) {
return res.status(403).json({
error: 'This endpoint is not available in production'
});
}
next();
}
module.exports = {
environmentMiddleware,
requireProduction,
blockInProduction
};
```
### Step 7: CI/CD Environment Configuration
```yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main, develop]
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- branch: develop
environment: staging
evernote_sandbox: 'true'
- branch: main
environment: production
evernote_sandbox: 'false'
steps:
- uses: actions/checkout@v4
- name: Set environment
if: github.ref == format('refs/heads/{0}', matrix.branch)
run: echo "DEPLOY_ENV=${{ matrix.environment }}" >> $GITHUB_ENV
- name: Deploy to ${{ matrix.environment }}
if: env.DEPLOY_ENV != ''
uses: ./.github/actions/deploy
with:
environment: ${{ matrix.environment }}
env:
EVERNOTE_CONSUMER_KEY: ${{ secrets[format('EVERNOTE_CONSUMER_KEY_{0}', matrix.environment)] }}
EVERNOTE_CONSUMER_SECRET: ${{ secrets[format('EVERNOTE_CONSUMER_SECRET_{0}', matrix.environment)] }}
EVERNOTE_SANDBOX: ${{ matrix.evernote_sandbox }}
```
### Step 8: Environment Health Check
```javascript
// routes/health.js
const config = require('../config');
const EvernoteClientFactory = require('../services/evernote-factory');
router.get('/health/environment', async (req, res) => {
const envInfo = EvernoteClientFactory.getEnvironmentInfo();
const validation = EvernoteClientFactory.validateEnvironment();
res.json({
status: validation.valid ? 'ok' : 'error',
environment: {
...envInfo,
nodeEnv: process.env.NODE_ENV,
version: process.env.APP_VERSION || 'unknown'
},
validation,
checks: {
database: await checkDatabase(),
redis: await checkRedis(),
evernote: await checkEvernoteConnectivity()
}
});
});
async function checkEvernoteConnectivity() {
try {
// Simple connectivity check - doesn't require auth
const https = require('https');
const host = config.get('evernote.sandbox')
? 'sandbox.evernote.com'
: 'www.evernote.com';
return new Promise((resolve) => {
const req = https.get(`https://${host}`, { timeout: 5000 }, (res) => {
resolve({ status: 'ok', host });
});
req.on('error', () => {
resolve({ status: 'error', host, message: 'Connection failed' });
});
req.on('timeout', () => {
req.destroy();
resolve({ status: 'error', host, message: 'Timeout' });
});
});
} catch (error) {
return { status: 'error', message: error.message };
}
}
```
## Output
- Environment-specific configuration files
- Configuration loader with environment variable support
- Environment-aware client factory
- Docker Compose for local development
- CI/CD environment configuration
- Health check endpoints
## Environment Checklist
```markdown
## Per-Environment Checklist
### Development
- [ ] Sandbox credentials configured
- [ ] Local database running
- [ ] Redis cache running
- [ ] Debug logging enabled
### Staging
- [ ] Sandbox credentials (separate from dev)
- [ ] Staging database provisioned
- [ ] Staging Redis provisioned
- [ ] Monitoring configured
### Production
- [ ] Production API key approved
- [ ] Secrets in secure storage
- [ ] Production database provisioned
- [ ] Production Redis provisioned
- [ ] Monitoring and alerting
- [ ] Backup procedures
```
## Resources
- [12 Factor App - Config](https://12factor.net/config)
- [Evernote Sandbox](https://sandbox.evernote.com)
- [Docker Compose](https://docs.docker.com/compose/)
## Next Steps
For observability setup, see `evernote-observability`.