Skip to content

Environment Variables

ScriptIt provides comprehensive environment variable management, allowing you to configure scripts for different environments, manage secrets securely, and access environment data within your scripts.

Overview

Environment variables in ScriptIt can be set through:

  1. Interactive prompts - --env-prompts flag or script variables export
  2. Command-line flags - --env VAR=value
  3. Configuration files - scriptit.config.js defaultParams
  4. .env files - Loaded automatically
  5. System environment variables - export VAR=value

Environment Variable Priority

Variables are applied in the following order (highest to lowest priority):

  1. Interactive prompts - Variables collected via --env-prompts or script variables export
  2. Command-line flags - --env VAR=value
  3. Configuration file - defaultParams property in config
  4. .env files - Loaded from project root and other configured files
  5. System environment variables - export VAR=value

Note: Variables that are already set (from any source) will not be prompted for interactively, ensuring no accidental overwrites.

Using .env Files

Basic .env File

Create a .env file in your project root:

env
# .env
NODE_ENV=development
DEBUG=true
API_KEY=your-api-key-here
DATABASE_URL=postgresql://localhost:5432/mydb
PORT=3000

# Comments are supported
# Use quotes for values with spaces
APP_NAME="My ScriptIt App"
DESCRIPTION="A powerful script runner"

# Multi-line values (use quotes)
PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7...
-----END PRIVATE KEY-----"

Environment-Specific .env Files

Create different .env files for different environments:

project/
├── .env                    # Default/development
├── .env.local             # Local overrides (gitignored)
├── .env.development       # Development environment
├── .env.staging           # Staging environment
├── .env.production        # Production environment
└── .env.test              # Test environment

.env.development

env
NODE_ENV=development
DEBUG=true
API_URL=http://localhost:3000
DATABASE_URL=postgresql://localhost:5432/myapp_dev
LOG_LEVEL=debug

.env.production

env
NODE_ENV=production
DEBUG=false
API_URL=https://api.myapp.com
DATABASE_URL=postgresql://prod-server:5432/myapp_prod
LOG_LEVEL=warn

Loading Environment-Specific Files

bash
# Load development environment
NODE_ENV=development scriptit exec script.js

# Load production environment
NODE_ENV=production scriptit exec script.js

# Load staging environment
NODE_ENV=staging scriptit exec script.js

Accessing Environment Variables

In Scripts

Access environment variables through the context.env object:

javascript
// script.js
export async function execute(context) {
  const console = context.console || global.console;
  
  // Access environment variables
  const nodeEnv = context.env.NODE_ENV;
  const apiKey = context.env.API_KEY;
  const dbUrl = context.env.DATABASE_URL;
  
  console.log('Environment:', nodeEnv);
  console.log('API Key:', apiKey ? '***' : 'Not set');
  console.log('Database URL:', dbUrl ? '***' : 'Not set');
  
  // Validate required variables
  if (!apiKey) {
    throw new Error('API_KEY environment variable is required');
  }
  
  return { environment: nodeEnv, hasApiKey: !!apiKey };
}

Environment Validation

javascript
// validate-env.js
export const description = "Validate required environment variables";

const REQUIRED_VARS = [
  'NODE_ENV',
  'API_KEY',
  'DATABASE_URL'
];

const OPTIONAL_VARS = [
  'DEBUG',
  'PORT',
  'LOG_LEVEL'
];

export async function execute(context) {
  const console = context.console || global.console;
  
  console.info('🔍 Validating environment variables...');
  
  const missing = [];
  const present = [];
  
  // Check required variables
  for (const varName of REQUIRED_VARS) {
    if (context.env[varName]) {
      present.push(varName);
      console.log(`✅ ${varName}: Set`);
    } else {
      missing.push(varName);
      console.error(`❌ ${varName}: Missing`);
    }
  }
  
  // Check optional variables
  for (const varName of OPTIONAL_VARS) {
    if (context.env[varName]) {
      console.log(`✅ ${varName}: ${context.env[varName]}`);
    } else {
      console.warn(`⚠️ ${varName}: Not set (optional)`);
    }
  }
  
  if (missing.length > 0) {
    console.error(`❌ Missing required variables: ${missing.join(', ')}`);
    throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
  }
  
  console.info('✅ All required environment variables are set');
  return { required: present, missing, environment: context.env.NODE_ENV };
}

Command-Line Environment Variables

Setting Variables

bash
# Single variable
scriptit exec --env NODE_ENV=production script.js

# Multiple variables
scriptit exec --env NODE_ENV=production --env DEBUG=false --env PORT=8080 script.js

# With spaces (use quotes)
scriptit exec --env APP_NAME="My App" --env DESCRIPTION="A great app" script.js

Interactive Environment Prompts

ScriptIt supports interactive collection of environment variables, providing a secure way to input sensitive data without exposing it in command history or scripts.

Using --env-prompts Flag

bash
# Prompt for specific variables before execution
scriptit exec --env-prompts API_KEY,SECRET_TOKEN script.js

# Space-separated format also works
scriptit exec --env-prompts API_KEY SECRET_TOKEN script.js

# Mix static and prompted variables
scriptit exec --env NODE_ENV=production --env-prompts API_KEY,DATABASE_PASSWORD script.js

# Works with TUI as well
scriptit run --env-prompts DATABASE_URL,API_KEY

Declarative Variable Definition in Scripts

Scripts can declare their required environment variables using the variables export:

javascript
// deployment-script.js
export const description = "Deploy application with secure prompts";

// Full definition with custom prompts and types
export const variables = [
  { name: 'API_KEY', message: 'Enter your API key:', type: 'password' },
  { name: 'DEPLOY_ENV', message: 'Target environment (prod/staging):', type: 'input' },
  'CONFIRM_DEPLOY'  // Shorthand - generates default prompt
];

export async function execute(context) {
  const console = context.console || global.console;
  
  // Variables are automatically prompted and available in context.env
  console.log(`Deploying to: ${context.env.DEPLOY_ENV}`);
  console.log(`API Key: ${context.env.API_KEY ? '***hidden***' : 'Not set'}`);
  
  if (context.env.CONFIRM_DEPLOY?.toLowerCase() !== 'yes') {
    console.warn('Deployment cancelled by user');
    return { cancelled: true };
  }
  
  // Proceed with deployment...
  return { success: true };
}

Variable Types

  • input (default): Regular text input, content visible as typed
  • password: Hidden input with masked characters (*)

Smart Variable Detection

ScriptIt automatically detects which variables need to be collected:

  • Skip existing - Variables already set in environment are not prompted
  • Combine sources - CLI --env-prompts + script variables export
  • Precedence handling - CLI prompts take precedence over script declarations
  • Security first - Prompted values are never logged or displayed

Combining with Other Options

bash
# With runtime selection
scriptit --runtime=bun exec --env NODE_ENV=production script.js

# With config file
scriptit exec --config prod.config.js --env API_KEY=secret script.js

# With working directory
scriptit --pwd /app exec --env NODE_ENV=production deploy.js

Configuration File Environment Variables

Basic Configuration

javascript
// scriptit.config.js
export default {
  env: {
    NODE_ENV: 'development',
    DEBUG: 'true',
    API_URL: 'http://localhost:3000',
    LOG_LEVEL: 'debug'
  }
}

Dynamic Configuration

javascript
// scriptit.config.js
export default {
  env: {
    NODE_ENV: process.env.NODE_ENV || 'development',
    DEBUG: process.env.DEBUG || 'false',
    API_URL: process.env.API_URL || 'http://localhost:3000',
    
    // Computed values
    IS_PRODUCTION: process.env.NODE_ENV === 'production',
    IS_DEVELOPMENT: process.env.NODE_ENV === 'development',
    
    // Default values with fallbacks
    PORT: process.env.PORT || '3000',
    HOST: process.env.HOST || 'localhost'
  }
}

Environment-Specific Configuration

javascript
// scriptit.config.js
const environments = {
  development: {
    env: {
      NODE_ENV: 'development',
      DEBUG: 'true',
      API_URL: 'http://localhost:3000',
      LOG_LEVEL: 'debug'
    }
  },
  
  staging: {
    env: {
      NODE_ENV: 'staging',
      DEBUG: 'false',
      API_URL: 'https://staging-api.myapp.com',
      LOG_LEVEL: 'info'
    }
  },
  
  production: {
    env: {
      NODE_ENV: 'production',
      DEBUG: 'false',
      API_URL: 'https://api.myapp.com',
      LOG_LEVEL: 'warn'
    }
  }
};

const currentEnv = process.env.NODE_ENV || 'development';
export default environments[currentEnv];

ScriptIt-Specific Environment Variables

Built-in Variables

VariableDescriptionExample
SCRIPTIT_RUNTIMEDefault runtime to usebun, deno, node
SCRIPTIT_DEBUGEnable debug modetrue, false
SCRIPTIT_CONFIGPath to config file./custom.config.js
SCRIPTIT_SCRIPTS_DIRScripts directory./src/scripts
SCRIPTIT_TMP_DIRTemporary directory./temp

Usage Examples

bash
# Set default runtime
export SCRIPTIT_RUNTIME=bun
scriptit exec script.js

# Enable debug mode
export SCRIPTIT_DEBUG=true
scriptit run

# Custom config file
export SCRIPTIT_CONFIG=./prod.config.js
scriptit exec deploy.js

# Custom directories
export SCRIPTIT_SCRIPTS_DIR=./src/scripts
export SCRIPTIT_TMP_DIR=./temp
scriptit run

Environment Variable Patterns

1. Configuration Pattern

javascript
// config-script.js
export const description = "Configuration management script";

export async function execute(context) {
  const console = context.console || global.console;
  
  const config = {
    environment: context.env.NODE_ENV || 'development',
    debug: context.env.DEBUG === 'true',
    api: {
      url: context.env.API_URL || 'http://localhost:3000',
      key: context.env.API_KEY,
      timeout: parseInt(context.env.API_TIMEOUT || '5000')
    },
    database: {
      url: context.env.DATABASE_URL,
      pool: {
        min: parseInt(context.env.DB_POOL_MIN || '2'),
        max: parseInt(context.env.DB_POOL_MAX || '10')
      }
    }
  };
  
  console.info('📋 Configuration loaded:');
  console.log('Environment:', config.environment);
  console.log('Debug mode:', config.debug);
  console.log('API URL:', config.api.url);
  console.log('API Key:', config.api.key ? '***' : 'Not set');
  
  return config;
}

2. Feature Flags Pattern

javascript
// feature-flags.js
export const description = "Feature flags management";

export async function execute(context) {
  const console = context.console || global.console;
  
  const features = {
    newUI: context.env.FEATURE_NEW_UI === 'true',
    betaFeatures: context.env.FEATURE_BETA === 'true',
    analytics: context.env.FEATURE_ANALYTICS !== 'false', // Default true
    debugging: context.env.FEATURE_DEBUG === 'true'
  };
  
  console.info('🚩 Feature flags:');
  Object.entries(features).forEach(([name, enabled]) => {
    const status = enabled ? '✅ Enabled' : '❌ Disabled';
    console.log(`${name}: ${status}`);
  });
  
  return features;
}

3. Secrets Management Pattern

javascript
// secrets.js
export const description = "Secure secrets management";

export async function execute(context) {
  const console = context.console || global.console;
  
  const secrets = {
    apiKey: context.env.API_KEY,
    dbPassword: context.env.DB_PASSWORD,
    jwtSecret: context.env.JWT_SECRET,
    encryptionKey: context.env.ENCRYPTION_KEY
  };
  
  // Validate all secrets are present
  const missingSecrets = Object.entries(secrets)
    .filter(([name, value]) => !value)
    .map(([name]) => name);
  
  if (missingSecrets.length > 0) {
    console.error('❌ Missing secrets:', missingSecrets.join(', '));
    throw new Error(`Missing required secrets: ${missingSecrets.join(', ')}`);
  }
  
  console.info('🔐 All secrets loaded successfully');
  
  // Return without exposing actual values
  return {
    secretsLoaded: Object.keys(secrets),
    allPresent: missingSecrets.length === 0
  };
}

Environment Variable Security

Best Practices

  1. Never commit secrets to version control:
bash
# .gitignore
.env.local
.env.*.local
.env.production
*.key
*.pem
  1. Use different files for different environments:
javascript
// ✅ Good - environment-specific files
.env.development    # Development secrets
.env.staging       # Staging secrets
.env.production    # Production secrets (never committed)

// ❌ Bad - single file with all secrets
.env               # Contains production secrets
  1. Validate required variables:
javascript
// ✅ Good - validate at startup
export async function execute(context) {
  const required = ['API_KEY', 'DATABASE_URL', 'JWT_SECRET'];
  const missing = required.filter(name => !context.env[name]);
  
  if (missing.length > 0) {
    throw new Error(`Missing: ${missing.join(', ')}`);
  }
}

// ❌ Bad - fail silently
export async function execute(context) {
  const apiKey = context.env.API_KEY || 'default-key';
}

Secret Rotation

javascript
// secret-rotation.js
export const description = "Check for secret rotation needs";

export async function execute(context) {
  const console = context.console || global.console;
  
  const secrets = [
    { name: 'API_KEY', lastRotated: context.env.API_KEY_ROTATED },
    { name: 'JWT_SECRET', lastRotated: context.env.JWT_SECRET_ROTATED },
    { name: 'DB_PASSWORD', lastRotated: context.env.DB_PASSWORD_ROTATED }
  ];
  
  const now = new Date();
  const rotationThreshold = 90 * 24 * 60 * 60 * 1000; // 90 days
  
  secrets.forEach(secret => {
    if (!secret.lastRotated) {
      console.warn(`⚠️ ${secret.name}: No rotation date set`);
      return;
    }
    
    const lastRotated = new Date(secret.lastRotated);
    const daysSince = Math.floor((now - lastRotated) / (24 * 60 * 60 * 1000));
    
    if (daysSince > 90) {
      console.error(`❌ ${secret.name}: Needs rotation (${daysSince} days old)`);
    } else {
      console.log(`✅ ${secret.name}: OK (${daysSince} days old)`);
    }
  });
  
  return { secrets, checkDate: now.toISOString() };
}

Environment Variable Templates

Development Template

env
# .env.development
NODE_ENV=development
DEBUG=true

# API Configuration
API_URL=http://localhost:3000
API_KEY=dev-api-key-123
API_TIMEOUT=10000

# Database Configuration
DATABASE_URL=postgresql://localhost:5432/myapp_dev
DB_POOL_MIN=2
DB_POOL_MAX=10

# Feature Flags
FEATURE_NEW_UI=true
FEATURE_BETA=true
FEATURE_ANALYTICS=false
FEATURE_DEBUG=true

# Logging
LOG_LEVEL=debug
LOG_FORMAT=pretty

# Development Tools
HOT_RELOAD=true
SOURCE_MAPS=true

Production Template

env
# .env.production (DO NOT COMMIT)
NODE_ENV=production
DEBUG=false

# API Configuration
API_URL=https://api.myapp.com
API_KEY=prod-api-key-secure
API_TIMEOUT=5000

# Database Configuration
DATABASE_URL=postgresql://prod-server:5432/myapp_prod
DB_POOL_MIN=5
DB_POOL_MAX=50

# Feature Flags
FEATURE_NEW_UI=true
FEATURE_BETA=false
FEATURE_ANALYTICS=true
FEATURE_DEBUG=false

# Logging
LOG_LEVEL=warn
LOG_FORMAT=json

# Security
JWT_SECRET=super-secure-jwt-secret
ENCRYPTION_KEY=32-char-encryption-key-here

Troubleshooting

Common Issues

Variable not found:

bash
 ScriptIt Error: Required environment variable API_KEY not found
💡 Set the variable: export API_KEY=your-key
💡 Or add to .env file: API_KEY=your-key

Invalid .env file:

bash
 ScriptIt Error: Invalid .env file syntax at line 5
💡 Check for missing quotes or invalid characters

Variable override conflicts:

bash
⚠️ Warning: API_KEY overridden by command line
💡 Command-line --env flags take precedence over .env files

Debug Environment Variables

Use debug mode to see environment variable loading:

bash
scriptit --debug exec script.js

Output includes:

  • Loaded .env files
  • Environment variable sources
  • Final merged environment
  • Variable precedence information

Environment Variable Inspection

javascript
// inspect-env.js
export const description = "Inspect environment variables";

export async function execute(context) {
  const console = context.console || global.console;
  
  console.info('🔍 Environment Variables:');
  
  // Group by prefix
  const groups = {};
  Object.keys(context.env).forEach(key => {
    const prefix = key.split('_')[0];
    if (!groups[prefix]) groups[prefix] = [];
    groups[prefix].push(key);
  });
  
  // Display grouped variables
  Object.entries(groups).forEach(([prefix, keys]) => {
    console.log(`\n📁 ${prefix}:`);
    keys.forEach(key => {
      const value = context.env[key];
      const displayValue = key.toLowerCase().includes('secret') || 
                          key.toLowerCase().includes('key') || 
                          key.toLowerCase().includes('password')
                          ? '***' : value;
      console.log(`  ${key}: ${displayValue}`);
    });
  });
  
  return { totalVariables: Object.keys(context.env).length, groups };
}

Released under the MIT License.