Documentation/Jutsu/Mise/ skills /mise-environment-management

📖 mise-environment-management

Use when managing environment variables and project settings with Mise. Covers env configuration, direnv replacement, and per-directory settings.



Overview

Managing environment variables, project settings, and directory-specific configuration with Mise.

Basic Environment Variables

Defining Environment Variables

# mise.toml
[env]
NODE_ENV = "production"
DATABASE_URL = "postgresql://localhost/myapp"
API_KEY = "development-key"
LOG_LEVEL = "info"

Loading Environment

# Activate mise environment
mise activate bash  # Or zsh, fish

# Or use mise exec
mise exec -- node app.js

# Or mise run
mise run start

Advanced Environment Configuration

Template Variables

[env]
PROJECT_ROOT = "{{ config_root }}"
DATA_DIR = "{{ config_root }}/data"
LOG_FILE = "{{ config_root }}/logs/app.log"

Environment File Loading

[env]
_.file = ".env"
_.file = [".env", ".env.local"]

Conditional Environment Variables

[env]
# Set based on other variables
DATABASE_URL = "postgresql://{{ env.DB_HOST | default(value='localhost') }}/{{ env.DB_NAME }}"

Path Management

Adding to PATH

[env]
_.path = [
  "{{ config_root }}/bin",
  "{{ config_root }}/scripts",
  "/usr/local/bin"
]

Library Paths

[env]
LD_LIBRARY_PATH = "{{ config_root }}/lib:$LD_LIBRARY_PATH"
DYLD_LIBRARY_PATH = "{{ config_root }}/lib:$DYLD_LIBRARY_PATH"

Tool-Specific Environments

Python Virtual Environment

[tools]
python = "3.12"

[env]
_.python.venv = { path = ".venv", create = true }
VIRTUAL_ENV = "{{ config_root }}/.venv"

Node.js Environment

[tools]
node = "20.10.0"

[env]
NODE_ENV = "development"
NODE_OPTIONS = "--max-old-space-size=4096"
NPM_CONFIG_PREFIX = "{{ config_root }}/.npm-global"

Go Environment

[tools]
go = "1.21"

[env]
GOPATH = "{{ config_root }}/.go"
GOBIN = "{{ config_root }}/.go/bin"
GO111MODULE = "on"

Replacing direnv

Basic direnv Replacement

# Instead of .envrc
# export DATABASE_URL=postgresql://localhost/myapp
# export NODE_ENV=development

# Use mise.toml
[env]
DATABASE_URL = "postgresql://localhost/myapp"
NODE_ENV = "development"

Allowed Directories

# mise.toml - mark as trusted
[settings]
experimental_monorepo_root = true  # Trust subdirectories

Watch File Changes

Mise automatically reloads when mise.toml changes, similar to direnv.

Hierarchical Configuration

Global Settings

# ~/.config/mise/config.toml
[env]
EDITOR = "code"
GIT_AUTHOR_NAME = "Your Name"
GIT_AUTHOR_EMAIL = "you@example.com"

Project Settings

# ~/projects/myapp/mise.toml
[env]
PROJECT_NAME = "myapp"
DATABASE_URL = "postgresql://localhost/myapp"

Local Overrides

# ~/projects/myapp/mise.local.toml (gitignored)
[env]
DATABASE_URL = "postgresql://localhost/myapp-dev"
DEBUG = "true"

Sensitive Data

Environment Files

# mise.toml
[env]
_.file = ".env.local"  # Gitignored file with secrets
# .env.local (add to .gitignore)
API_KEY=secret-key-here
DATABASE_PASSWORD=secret-password

Using System Environment

[env]
# Reference existing environment variables
API_KEY = "$API_KEY"
DATABASE_URL = "$DATABASE_URL"

Secure Secrets Management

# Don't commit secrets to mise.toml
# Instead, reference from external secret managers

# Example with 1Password CLI
mise exec -- op run -- node app.js

# Or load from encrypted file
mise exec -- sops exec-env .env.encrypted -- node app.js

Mise Environment Variables

Built-in Variables

Mise provides these variables automatically:

$MISE_ORIGINAL_CWD      # Directory where mise was invoked
$MISE_CONFIG_ROOT       # Directory containing mise.toml
$MISE_PROJECT_ROOT      # Project root directory
$MISE_DATA_DIR          # Mise data directory
$MISE_CACHE_DIR         # Mise cache directory

Using in Configuration

[env]
PROJECT_ROOT = "{{ env.MISE_PROJECT_ROOT }}"
CONFIG_FILE = "{{ env.MISE_CONFIG_ROOT }}/config.yaml"

Configuration Validation

Check Current Environment

# Show current environment
mise env

# Show specific variable
mise env DATABASE_URL

# Export as shell commands
mise env -s bash > .env.sh
source .env.sh

Verify Configuration

# Check loaded config files
mise config

# Show resolved settings
mise settings

Best Practices

Separate Public and Private Config

# mise.toml (committed)
[env]
NODE_ENV = "development"
LOG_LEVEL = "info"
API_URL = "https://api.example.com"

# mise.local.toml (gitignored)
[env]
API_KEY = "secret-key"
DATABASE_PASSWORD = "secret-password"

Use Descriptive Variable Names

# Good: Clear, descriptive names
[env]
DATABASE_CONNECTION_POOL_SIZE = "10"
API_REQUEST_TIMEOUT_MS = "5000"
FEATURE_FLAG_NEW_UI = "true"

# Avoid: Vague abbreviations
[env]
DB_POOL = "10"
TIMEOUT = "5000"
FLAG = "true"

Document Required Variables

# mise.toml
[env]
# Database configuration (required)
DATABASE_URL = "postgresql://localhost/myapp"

# API keys (set in mise.local.toml)
# API_KEY = "your-key-here"

# Optional feature flags
FEATURE_ANALYTICS = "false"

Use Templates for Paths

# Good: Relative to config root
[env]
DATA_DIR = "{{ config_root }}/data"
LOGS_DIR = "{{ config_root }}/logs"

# Avoid: Hardcoded paths
[env]
DATA_DIR = "/Users/me/project/data"

Common Patterns

Multi-Environment Setup

# mise.toml - base configuration
[env]
APP_NAME = "myapp"
LOG_FORMAT = "json"

# mise.development.toml
[env]
NODE_ENV = "development"
DEBUG = "true"
DATABASE_URL = "postgresql://localhost/myapp_dev"

# mise.production.toml
[env]
NODE_ENV = "production"
DEBUG = "false"
DATABASE_URL = "postgresql://prod-server/myapp"
# Switch environments
ln -sf mise.development.toml mise.local.toml
# Or
ln -sf mise.production.toml mise.local.toml

Database Configuration

[env]
DATABASE_HOST = "localhost"
DATABASE_PORT = "5432"
DATABASE_NAME = "myapp"
DATABASE_USER = "postgres"
DATABASE_URL = "postgresql://{{ env.DATABASE_USER }}:{{ env.DATABASE_PASSWORD }}@{{ env.DATABASE_HOST }}:{{ env.DATABASE_PORT }}/{{ env.DATABASE_NAME }}"

Feature Flags

[env]
# Feature toggles
FEATURE_NEW_DASHBOARD = "true"
FEATURE_BETA_API = "false"
FEATURE_EXPERIMENTAL_CACHE = "true"

# Feature rollout percentages
FEATURE_NEW_CHECKOUT_ROLLOUT = "25"

CI/CD Environment

# mise.toml
[env]
NODE_ENV = "{{ env.CI | default(value='development') }}"
SKIP_PREFLIGHT_CHECK = "{{ env.CI | default(value='false') }}"

Anti-Patterns

Don't Commit Secrets

# Bad: Secrets in committed file
[env]
API_KEY = "sk-secret-key-12345"
DATABASE_PASSWORD = "password123"

# Good: Reference from secure location
[env]
_.file = ".env.local"  # Gitignored

Don't Duplicate Global Config

# Bad: Repeating global settings in every project
# ~/project-a/mise.toml
[env]
EDITOR = "code"
GIT_AUTHOR_NAME = "Your Name"

# ~/project-b/mise.toml
[env]
EDITOR = "code"
GIT_AUTHOR_NAME = "Your Name"

# Good: Use global config
# ~/.config/mise/config.toml
[env]
EDITOR = "code"
GIT_AUTHOR_NAME = "Your Name"

Don't Hardcode Environment Names

# Bad: Hardcoded check
[tasks.deploy]
run = '''
if [ "$NODE_ENV" = "production" ]; then
  ./deploy-prod.sh
fi
'''

# Good: Use configuration
[tasks.deploy]
env = { DEPLOYMENT_TARGET = "production" }
run = "./deploy.sh"

Advanced Patterns

Dynamic Environment Loading

[env]
# Load environment based on git branch
BRANCH = "{{ exec(command='git branch --show-current') }}"
DEPLOY_ENV = "{{ env.BRANCH | replace(from='main', to='production') | replace(from='develop', to='staging') }}"

Computed Variables

[env]
PROJECT_NAME = "myapp"
NAMESPACE = "{{ env.PROJECT_NAME }}-{{ env.ENVIRONMENT }}"
REDIS_URL = "redis://{{ env.NAMESPACE }}-redis:6379"

Environment Inheritance

# Base configuration
[env]
LOG_LEVEL = "info"
CACHE_TTL = "3600"

# Override in specific contexts
[env.production]
LOG_LEVEL = "warn"
CACHE_TTL = "7200"

Related Skills

  • task-configuration: Using environment variables in tasks
  • tool-management: Tool-specific environment configuration