Skip to main content

Diff & patch environment configs across local/remote (.env, YAML, config.py) plus single-key editor.

Project description

envdiff / envset

Powerful CLI tools for comparing and editing environment configurations across local and remote systems

Python 3.8+ License: MIT Code Coverage

envdiff and envset are command-line tools that help you manage environment configurations across different file formats (.env, YAML, Python config.py) and locations (local files or remote servers via SSH).


โœจ Features

envdiff โ€” Compare & Patch

  • ๐Ÿ” Compare environment configs between source and target files
  • ๐Ÿ“Š Detect missing, extra, and different keys
  • ๐Ÿ”ง Generate patches in multiple formats (export, dotenv, PowerShell)
  • โœ… CI/CD integration with --check flag (exits non-zero on differences)
  • ๐ŸŽฏ Filter keys with regex patterns (--include / --exclude)
  • ๐Ÿ“ Apply changes directly to .env files with automatic backups
  • ๐ŸŒ Remote support via SSH/SCP for both source and target
  • ๐Ÿ“‹ JSON output for machine-readable reports

envset โ€” Single Key Editor

  • โœ๏ธ Edit one key across multiple files simultaneously
  • ๐Ÿ“ Multi-format support: .env, YAML, Python config.py
  • ๐Ÿ”„ In-place rewriting for existing constants and dict assignments
  • ๐ŸŽฏ Nested keys support for YAML and Python dicts (dot notation)
  • ๐ŸŒ Remote editing via SSH/SCP
  • ๐Ÿ’พ Automatic backups before modifications
  • ๐Ÿ”ข JSON values support for typed data

๐Ÿš€ Quick Start

Installation

Option 1: pipx (Recommended for CLI tools)

pipx install envdiff-tool

Option 2: pip

pip install envdiff-tool

Option 3: From source

git clone https://github.com/talaatmagdyx/envdiff_fresh.git
cd envdiff_fresh
pip install -e .

Quick Example

# Compare YAML config with .env file
envdiff --source config.yml --target .env --patch-format export

# Set a key across multiple files
envset --files .env config.yml --key LOG_LEVEL --value warning

๐Ÿ“– Documentation

Table of Contents


๐Ÿ” envdiff: Compare & Patch

Compare environment configurations and generate patches to synchronize them.

Basic Usage

# Compare source YAML with target .env
envdiff --source examples/source.yml --target examples/target.env

# Output:
# Missing on target (2):
#   - database.host
#   - database.port
# 
# Extra on target (2):
#   - DATABASE_HOST
#   - DATABASE_PORT
# 
# Different values (2):
#   - APP_MODE
#   - FEATURE_X

Generate Patches

# Generate export format patch
envdiff --source examples/source.yml --target examples/target.env --patch-format export

# Output:
# # Patch (export) โ€” apply on target to add/update keys
# export database.host='db.prod'
# export database.port='5432'
# export APP_MODE='production'
# export FEATURE_X='True'

Available patch formats:

  • export โ€” Shell export statements (Bash/Zsh)
  • dotenv โ€” Standard .env format
  • powershell โ€” PowerShell environment variables

Apply Changes Directly

# Apply changes to target .env (creates backup automatically)
envdiff --source config.yml --target .env --apply

# Dry-run to preview changes
envdiff --source config.yml --target .env --apply --apply-dry-run

Filter Keys with Regex

# Only check keys starting with APP_
envdiff --source config.yml --target .env --include '^APP_'

# Exclude secret keys
envdiff --source config.yml --target .env --exclude 'SECRET|PASSWORD|KEY'

# Combine filters
envdiff --source config.yml --target .env \
  --include '^APP_' \
  --exclude 'SECRET'

Prefix Filtering

# Only compare keys with specific prefix
envdiff --source config.yml --target .env --only-prefix DATABASE

# Ignore keys with specific prefix
envdiff --source config.yml --target .env --ignore-prefix DEBUG

JSON Output

Get machine-readable output for automation:

envdiff --source config.yml --target .env --format json

# Output:
# {
#   "source": "config.yml",
#   "target": ".env",
#   "summary": {
#     "missing": 2,
#     "extra": 1,
#     "different": 1,
#     "same": 5
#   },
#   "keys": {
#     "missing": ["APP_NAME", "API_KEY"],
#     "extra": ["OLD_KEY"],
#     "different": ["DATABASE_URL"]
#   },
#   "patch": {
#     "format": "export",
#     "lines": ["export APP_NAME='myapp'", ...]
#   }
# }

Case Sensitivity

# Case-insensitive comparison (default)
envdiff --source config.yml --target .env --case-sensitive false

# Case-sensitive comparison
envdiff --source config.yml --target .env --case-sensitive true

โœ๏ธ envset: Single Key Editor

Edit a single key across multiple files in different formats.

Basic Usage

# Set LOG_LEVEL in multiple files
envset --files .env config.yml --key LOG_LEVEL --value warning

Supported File Types

.env Files

envset --files .env --key DATABASE_URL --value postgres://localhost/db

YAML Files (Nested Keys)

# Set nested key using dot notation
envset --files config.yml --key app.database.host --value db.example.com

# Set with JSON value (for typed data)
envset --files config.yml --key app.timeout --value '30' --json

Python config.py Files

UPPERCASE Constants:

# Add or update a constant
envset --files config.py --key RABBITMQ_USER --value admin

# In-place rewrite existing constant
envset --files config.py --key RABBITMQ_USER --value root --rewrite

CONFIG/ENV Dictionaries:

# Set nested dict key
envset --files config.py --key database.host --value db.new

# In-place rewrite existing dict assignment
envset --files config.py --key database.host --value db.new --rewrite

Multiple Files

# Update the same key across multiple files
envset --files .env config.yml config.py \
  --key API_KEY \
  --value "new-secret-key"

JSON Values

# Set complex JSON values
envset --files config.yml \
  --key app.features \
  --value '{"feature1": true, "feature2": false}' \
  --json

Dry Run

# Preview changes without modifying files
envset --files config.py --key RABBITMQ_USER --value admin --dry-run

๐ŸŒ Remote Operations

Both tools support remote files via SSH/SCP.

SSH Configuration

# Basic remote path
envdiff --source user@server:/path/to/config.yml --target .env

# With SSH port
envdiff --source user@server:/path/config.yml \
  --target .env \
  --source-ssh-port 2222

# With SSH identity file
envdiff --source user@server:/path/config.yml \
  --target .env \
  --source-ssh-identity ~/.ssh/id_rsa

# With SSH options
envdiff --source user@server:/path/config.yml \
  --target .env \
  --source-ssh-extra "StrictHostKeyChecking=no,UserKnownHostsFile=/dev/null"

Remote Examples

# Compare local YAML with remote .env
envdiff --source config.yml --target user@prod:/srv/app/.env

# Set key on remote config.py
envset --files user@prod:/opt/app/config.py \
  --key RABBITMQ_USER \
  --value root \
  --rewrite

# Apply changes to remote .env
envdiff --source config.yml \
  --target user@prod:/srv/app/.env \
  --apply

๐Ÿ”„ CI/CD Integration

Use --check flag to fail CI builds when configurations don't match.

Basic CI Check

# Exit with code 5 if differences exist
envdiff --source config.yml --target .env --check

# In CI script
if ! envdiff --source config.yml --target .env --check; then
  echo "Configuration mismatch detected!"
  exit 1
fi

With Filters

# Only check specific keys
envdiff --source config.yml --target .env \
  --include '^APP_' \
  --check

JSON Output for CI

# Get structured output for CI scripts
envdiff --source config.yml --target .env \
  --format json \
  --check \
  --patch-format export

# Exit code 5 if differences remain after filtering

GitHub Actions Example

- name: Check environment configuration
  run: |
    envdiff \
      --source config/production.yml \
      --target .env \
      --include '^APP_' \
      --exclude 'SECRET' \
      --check \
      --format json

๐ŸŽฏ Advanced Usage

Custom Source/Target Types

# Explicitly specify file types
envdiff --source config.yml \
  --target config.py \
  --source-type yaml \
  --target-type py

Show Same Values

# Include keys with matching values in output
envdiff --source config.yml --target .env --show-same

Keys-Only JSON Output

# Get only the keys JSON (without full report)
envdiff --source examples/source.yml --target examples/target.env --keys-json

# Output (JSON followed by text summary):
# {
#   "missing": ["database.host", "database.port"],
#   "extra": ["DATABASE_HOST", "DATABASE_PORT"],
#   "different": ["APP_MODE", "FEATURE_X"],
#   "same": null
# }

JSON-Only Mode

# Output only JSON (no human-readable text)
envdiff --source config.yml --target .env --json-only

Backup Control

# Disable automatic backups
envdiff --source config.yml --target .env --apply --apply-backup none

# Backups are enabled by default (format: .env.bak-20240101120000)

๐ŸชŸ Windows Support

Installation

# Install with pipx
pipx install envdiff-tool

# If pipx is not on PATH, add it:
$Env:Path += ';' + (python -m site --user-base) + '\Scripts'

PowerShell Patch Format

# Generate PowerShell patch
envdiff --source .\config.yml --target .\.env --patch-format powershell > patch.ps1

# Review and apply
Get-Content patch.ps1
. .\patch.ps1

Remote Operations from Windows

Windows 10+ includes OpenSSH, so remote operations work out of the box:

# Compare with remote file
envdiff --source .\config.yml --target user@server:/srv/app/.env

# Edit remote config
envset --files user@server:/opt/app/config.py --key RABBITMQ_USER --value root

๐Ÿ“ File Format Examples

.env Format

APP_NAME=myapp
DATABASE_URL=postgres://localhost/db
LOG_LEVEL=info

YAML Format

app:
  name: myapp
  database:
    url: postgres://localhost/db
logging:
  level: info

Python config.py Format

# Constants
RABBITMQ_USER = 'admin'
API_KEY = 'secret-key'

# Or dictionaries
CONFIG = {
    'database': {
        'host': 'localhost',
        'port': 5432
    }
}

๐Ÿ› ๏ธ Development

Setup

# Clone repository
git clone https://github.com/talaatmagdyx/envdiff_fresh.git
cd envdiff_fresh

# Install in editable mode with dev dependencies
pip install -e .[dev]

Running Tests

# Run all tests with coverage
pytest

# Tests require 100% coverage to pass

Linting

# Check code style
ruff check .

# Format code
ruff format .

# Check formatting without changes
ruff format --check .

Project Structure

envdiff_fresh/
โ”œโ”€โ”€ envdiff.py          # Main diff/patch tool
โ”œโ”€โ”€ envset.py           # Single key editor
โ”œโ”€โ”€ tests/              # Test suite (100% coverage)
โ”‚   โ”œโ”€โ”€ test_cli_integration.py
โ”‚   โ”œโ”€โ”€ test_coverage.py
โ”‚   โ”œโ”€โ”€ test_envset_configpy.py
โ”‚   โ””โ”€โ”€ test_parsers_and_patch.py
โ”œโ”€โ”€ examples/           # Example files
โ”‚   โ”œโ”€โ”€ example.env
โ”‚   โ”œโ”€โ”€ example.yaml
โ”‚   โ”œโ”€โ”€ source.yml
โ”‚   โ”œโ”€โ”€ target.env
โ”‚   โ””โ”€โ”€ target_config.py
โ”œโ”€โ”€ pyproject.toml      # Project configuration
โ”œโ”€โ”€ requirements.txt    # Runtime dependencies
โ””โ”€โ”€ README.md           # This file

๐Ÿ“ Examples Directory

The examples/ directory contains sample files for testing:

  • example.env โ€” Sample .env file
  • example.yaml โ€” Sample YAML configuration
  • source.yml โ€” Source file for diff examples
  • target.env โ€” Target file for diff examples
  • target_config.py โ€” Python config file example

Try them out:

envdiff --source examples/source.yml --target examples/target.env
envset --files examples/example.env examples/example.yaml --key LOG_LEVEL --value debug

๐Ÿšข Release Process

This repository includes GitHub Actions workflows for automated releases.

Standard Release

  1. Bump version in pyproject.toml
  2. Commit and tag:
    git add pyproject.toml
    git commit -m "chore: bump version to 0.1.1"
    git tag v0.1.1
    git push --follow-tags
    
  3. GitHub Actions will:
    • Build sdist and wheel packages
    • Create a GitHub Release
    • Upload to PyPI (if PYPI_API_TOKEN secret is set)

Release Candidate (RC)

  1. Bump version to RC format (e.g., 0.2.0rc1)
  2. Tag with RC pattern:
    git commit -am "chore: bump version to 0.2.0rc1"
    git tag v0.2.0-rc1
    git push --follow-tags
    
  3. GitHub Actions will:
    • Build packages
    • Create a GitHub Pre-Release
    • Upload to TestPyPI (if TEST_PYPI_API_TOKEN secret is set)

PyPI Tokens

Add tokens in GitHub repository settings:

  • Settings โ†’ Secrets and variables โ†’ Actions โ†’ New repository secret
  • PYPI_API_TOKEN โ€” For production releases
  • TEST_PYPI_API_TOKEN โ€” For release candidates

โ“ FAQ

Why do I need both tools?

  • envdiff: Compare configurations and generate patches for synchronization
  • envset: Quickly update a single key across multiple files

Can I use these tools with Docker?

Yes! Both tools work inside Docker containers. Mount your config files as volumes and run the commands.

How do I handle secrets?

Use --exclude to filter out secret keys from comparisons:

envdiff --source config.yml --target .env --exclude 'SECRET|PASSWORD|KEY'

Can I compare more than two files?

Currently, envdiff compares one source to one target. For multiple comparisons, run the command multiple times or use a script.

What Python versions are supported?

Python 3.8 and above.


๐Ÿ“„ License

MIT License โ€” see LICENSE file for details.


๐Ÿค Contributing

Contributions are welcome! Please ensure:

  • All tests pass (pytest)
  • Code coverage remains at 100%
  • Code follows style guidelines (ruff check)

๐Ÿ“š Additional Resources

  • Run envdiff --help or envset --help for full command-line options
  • Check examples/ directory for sample files
  • Review test files in tests/ for usage examples

Made with โค๏ธ for developers who care about configuration management

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

envdiff_tool-0.1.2.tar.gz (42.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

envdiff_tool-0.1.2-py3-none-any.whl (25.4 kB view details)

Uploaded Python 3

File details

Details for the file envdiff_tool-0.1.2.tar.gz.

File metadata

  • Download URL: envdiff_tool-0.1.2.tar.gz
  • Upload date:
  • Size: 42.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for envdiff_tool-0.1.2.tar.gz
Algorithm Hash digest
SHA256 3c3968930c581d58693c142950e1c3753ee5aef855b91420fd28334a554eba1e
MD5 8f8735c2555ac841f61a6bd9b832ea57
BLAKE2b-256 0c81de3a8bbddb07c370ce9de69404a3614025d3a567a2ff84c35b263be3c8fb

See more details on using hashes here.

File details

Details for the file envdiff_tool-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: envdiff_tool-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 25.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for envdiff_tool-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 651dc89d9af8af6ddac8974a3503ac59c6ce2d72f4e4e4484684b877606b8287
MD5 0b768b28d37baf91c095a5d8e4faec50
BLAKE2b-256 7640d95142a33141651f86e5cccc85290cb7731cc346b6ce8f25f196d0dd45f8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page