Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ on:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
tool:
- locust-compare
- config-utils
fail-fast: false
steps:
- uses: actions/checkout@v4

Expand All @@ -17,9 +23,10 @@ jobs:
with:
python-version: 3.12

- name: Install and test locust-compare
working-directory: tools/locust-compare
- name: Install and test ${{ matrix.tool }}
working-directory: tools/${{ matrix.tool }}
run: |
python -m pip install --upgrade pip
pip install -e .[dev]
pytest
python -m pytest -v

155 changes: 152 additions & 3 deletions tools/config-utils/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# config-utils

A CLI tool for capturing environment variables and Django settings in YAML format.
A CLI tool for capturing environment variables and Django settings in YAML format, plus performing set operations on YAML configuration files.

## Features

- **capture-env**: Capture all environment variables and export them to YAML
- **capture-django-settings**: Capture Django project settings and export them to YAML
- **Set Operations**: Compare and merge YAML files using set operations (union, intersect, diff, rdiff, symdiff)

## Installation

Expand Down Expand Up @@ -116,6 +117,124 @@ config-utils capture-django-settings

**Note**: This command must be run from your Django project directory or you must specify the path to `manage.py` using the `--manage-py` option.

### YAML Set Operations

Perform set operations on two YAML configuration files. All operations output valid YAML to stdout.

#### Commands

- `union`: Returns all keys (or key-value pairs) present in either file
- `intersect`: Returns only keys (or key-value pairs) present in both files
- `diff`: Returns keys (or key-value pairs) in file1 but not in file2 (A - B)
- `rdiff`: Returns keys (or key-value pairs) in file2 but not in file1 (B - A)
- `symdiff`: Returns keys (or key-value pairs) in either file but not in both (symmetric difference)

#### Options

- `--compare <mode>`: Comparison mode - `keys` or `kv` (key-values). Default: `kv`
- `kv`: Compares key-value pairs. Two entries match only if both key AND value are identical
- `keys`: Compares only keys. Values are ignored when determining matches
- `--depth <n>`: How many levels deep to compare. Default: `1`
- `1`: Root keys only
- `2`: Compare up to 2 levels (root and one level of nesting)
- `0`: Unlimited depth (fully flatten using dot notation)

#### Examples

**file1.yml:**
```yaml
database: postgres
port: 5432
debug: true
```

**file2.yml:**
```yaml
database: postgres
port: 3306
logging: verbose
```

**Find common configuration (intersect with key-values):**
```bash
config-utils intersect file1.yml file2.yml
# Output:
# database: postgres
```

**Find common keys regardless of values:**
```bash
config-utils intersect file1.yml file2.yml --compare keys
# Output:
# database: postgres
# port: 5432
```

**Find all unique configuration (union):**
```bash
config-utils union file1.yml file2.yml
# Output:
# database: postgres
# port: 5432
# debug: true
# logging: verbose
```

**Find what's in file1 but not file2 (diff):**
```bash
config-utils diff file1.yml file2.yml
# Output:
# port: 5432
# debug: true
```

**Find what's in file2 but not file1 (rdiff):**
```bash
config-utils rdiff file1.yml file2.yml
# Output:
# port: 3306
# logging: verbose
```

**Nested comparison with depth:**

**nested1.yml:**
```yaml
database:
host: localhost
port: 5432
app:
name: myapp
```

**nested2.yml:**
```yaml
database:
host: localhost
port: 3306
app:
name: myapp
```

```bash
# Depth 1 - root keys only
config-utils intersect nested1.yml nested2.yml --depth 1
# Output:
# database:
# host: localhost
# port: 5432
# app:
# name: myapp

# Depth 2 - compare nested keys
config-utils intersect nested1.yml nested2.yml --depth 2
# Output:
# database:
# host: localhost
# app:
# name: myapp
```

### Using with uvx

You can run the tool directly without installation:
Expand Down Expand Up @@ -152,15 +271,45 @@ uvx --from /path/to/config-utils config-utils capture-django-settings -m /path/t
cd config-utils

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

### Running Tests

The project includes comprehensive pytest tests for all set operations functionality.

```bash
# Run all tests
python -m pytest

# Run with coverage report
python -m pytest --cov=cli --cov-report=term-missing

# Run specific test class
python -m pytest tests/test_set_operations.py::TestUnionCommand -v

# Run specific test
python -m pytest tests/test_set_operations.py::TestUnionCommand::test_union_kv_mode -v
```

### Test Coverage

The test suite covers:
- All 5 set operations (union, intersect, diff, rdiff, symdiff)
- Both comparison modes (keys and kv)
- All depth levels (0, 1, 2+)
- Error handling (missing files, invalid YAML, non-dict roots)
- Edge cases (empty files, identical files, no matches)
- Helper functions (flatten_dict, unflatten_dict, make_hashable, perform_set_operation, load_yaml_file)

### Project Structure

```
config-utils/
├── cli.py
├── config_utils/
├── tests/
│ ├── __init__.py
│ └── test_set_operations.py
├── pyproject.toml
└── README.md
```
Expand Down
Loading