CtrlK
CommunityDocumentationLog inGet started
Tessl Logo

evernote-multi-env-setup

tessl install github:jeremylongshore/claude-code-plugins-plus-skills --skill evernote-multi-env-setup
github.com/jeremylongshore/claude-code-plugins-plus-skills

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".

Review Score

77%

Validation Score

11/16

Implementation Score

65%

Activation Score

90%

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

EnvironmentEvernote ModePurposeUsers
DevelopmentSandboxLocal developmentDevelopers
StagingSandboxIntegration testingQA team
ProductionProductionLive usersCustomers

Instructions

Step 1: Environment Configuration Files

# 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
// config/default.js
module.exports = {
  evernote: {
    requestTimeout: 30000,
    connectionTimeout: 10000,
    maxRetries: 3
  },
  cache: {
    ttl: 300,
    prefix: 'evernote:'
  },
  logging: {
    redactTokens: true
  }
};
// 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'
  }
};
// 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'
  }
};
// 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

# .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=
# .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
# .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
# .env.production
NODE_ENV=production
# ALL SECRETS MANAGED EXTERNALLY
EVERNOTE_SANDBOX=false
APP_URL=https://yourapp.com

Step 3: Configuration Loader

// 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

// 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

# 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

// 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

# .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

// 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

## 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
  • Evernote Sandbox
  • Docker Compose

Next Steps

For observability setup, see evernote-observability.