Skip to content

Commit 3b01b97

Browse files
Merge pull request #1 from SoundsSerious/cicd
Add GitHub Actions workflows, testing framework, and documentation for diffgetr library
2 parents 4a95756 + c35b280 commit 3b01b97

File tree

9 files changed

+879
-101
lines changed

9 files changed

+879
-101
lines changed

.claude/settings.local.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(mkdir:*)"
5+
],
6+
"deny": [],
7+
"ask": []
8+
}
9+
}

.github/workflows/main.yml

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
name: Build and Release
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
paths:
7+
- '**/*.py'
8+
- 'pyproject.toml'
9+
- 'requirements*.txt'
10+
11+
permissions:
12+
contents: write
13+
id-token: write # For PyPI trusted publishing
14+
15+
jobs:
16+
build-and-test:
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- name: Set up Python
22+
uses: actions/setup-python@v4
23+
with:
24+
python-version: '3.9'
25+
26+
- name: Install dependencies
27+
run: |
28+
python -m pip install --upgrade pip
29+
pip install build pytest toml
30+
31+
- name: Build package
32+
run: |
33+
python -m build
34+
35+
- name: Install package
36+
run: |
37+
pip install dist/*.whl
38+
39+
- name: Test installation
40+
run: |
41+
python -c "import diffgetr; print('Package imported successfully')"
42+
diffgetr --help || echo "CLI help command executed"
43+
44+
- name: Run unit tests
45+
run: |
46+
pytest tests/ -v
47+
48+
- name: Upload build artifacts
49+
uses: actions/upload-artifact@v3
50+
with:
51+
name: dist
52+
path: dist/
53+
54+
publish-pypi:
55+
runs-on: ubuntu-latest
56+
needs: build-and-test
57+
environment: release
58+
steps:
59+
- uses: actions/checkout@v4
60+
61+
- name: Download build artifacts
62+
uses: actions/download-artifact@v3
63+
with:
64+
name: dist
65+
path: dist/
66+
67+
- name: Publish to PyPI
68+
uses: pypa/gh-action-pypi-publish@release/v1
69+
with:
70+
verbose: true
71+
72+
create-github-release:
73+
runs-on: ubuntu-latest
74+
needs: [build-and-test, publish-pypi]
75+
steps:
76+
- uses: actions/checkout@v4
77+
with:
78+
fetch-depth: 0 # Fetch full history for changelog
79+
80+
- name: Set up Python
81+
uses: actions/setup-python@v4
82+
with:
83+
python-version: '3.9'
84+
85+
- name: Install dependencies
86+
run: |
87+
python -m pip install --upgrade pip
88+
pip install toml
89+
90+
- name: Get version and create release
91+
run: |
92+
python -c "
93+
import toml
94+
import os
95+
96+
# Read version from pyproject.toml
97+
with open('pyproject.toml', 'r') as f:
98+
data = toml.load(f)
99+
100+
version = data['project']['version']
101+
description = data['project']['description']
102+
103+
# Set environment variables for next step
104+
with open(os.environ['GITHUB_ENV'], 'a') as f:
105+
f.write(f'VERSION={version}\n')
106+
f.write(f'DESCRIPTION={description}\n')
107+
"
108+
109+
- name: Download build artifacts
110+
uses: actions/download-artifact@v3
111+
with:
112+
name: dist
113+
path: dist/
114+
115+
- name: Generate changelog
116+
run: |
117+
echo "## Changes" > CHANGELOG.md
118+
echo "" >> CHANGELOG.md
119+
120+
# Get commits since last tag
121+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
122+
if [ -n "$LAST_TAG" ]; then
123+
echo "Changes since $LAST_TAG:" >> CHANGELOG.md
124+
git log $LAST_TAG..HEAD --pretty=format:"- %s (%h)" >> CHANGELOG.md
125+
else
126+
echo "Initial release" >> CHANGELOG.md
127+
git log --pretty=format:"- %s (%h)" >> CHANGELOG.md
128+
fi
129+
130+
echo "" >> CHANGELOG.md
131+
echo "## Package Files" >> CHANGELOG.md
132+
echo "" >> CHANGELOG.md
133+
echo "The following files are available for download:" >> CHANGELOG.md
134+
for file in dist/*; do
135+
echo "- $(basename $file)" >> CHANGELOG.md
136+
done
137+
138+
- name: Create GitHub Release
139+
uses: softprops/action-gh-release@v1
140+
with:
141+
tag_name: v${{ env.VERSION }}
142+
name: Release v${{ env.VERSION }}
143+
body_path: CHANGELOG.md
144+
files: dist/*
145+
draft: false
146+
prerelease: false
147+
env:
148+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/pr.yml

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
name: Pull Request Checks
2+
3+
on:
4+
pull_request:
5+
branches: [ main ]
6+
paths:
7+
- '**/*.py'
8+
- 'pyproject.toml'
9+
- 'requirements*.txt'
10+
- '.github/workflows/**'
11+
12+
permissions:
13+
contents: write
14+
pull-requests: write
15+
16+
jobs:
17+
version-check:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- name: Set up Python
23+
uses: actions/setup-python@v4
24+
with:
25+
python-version: '3.9'
26+
27+
- name: Install dependencies
28+
run: |
29+
python -m pip install --upgrade pip
30+
pip install toml requests
31+
32+
- name: Check if version exists as GitHub release
33+
run: |
34+
python -c "
35+
import toml
36+
import requests
37+
import sys
38+
39+
# Read current version from pyproject.toml
40+
with open('pyproject.toml', 'r') as f:
41+
data = toml.load(f)
42+
43+
current_version = data['project']['version']
44+
print(f'Current version: {current_version}')
45+
46+
# Check if this version exists as a GitHub release
47+
repo = '${{ github.repository }}'
48+
url = f'https://api.github.com/repos/{repo}/releases/tags/v{current_version}'
49+
50+
response = requests.get(url)
51+
if response.status_code == 200:
52+
print(f'ERROR: Release v{current_version} already exists!')
53+
print('Please update the version in pyproject.toml before merging.')
54+
sys.exit(1)
55+
else:
56+
print(f'Version v{current_version} is available for release.')
57+
"
58+
59+
build-and-test:
60+
runs-on: ubuntu-latest
61+
needs: version-check
62+
steps:
63+
- uses: actions/checkout@v4
64+
65+
- name: Set up Python
66+
uses: actions/setup-python@v4
67+
with:
68+
python-version: '3.9'
69+
70+
- name: Install dependencies
71+
run: |
72+
python -m pip install --upgrade pip
73+
pip install build pytest black
74+
75+
- name: Build package
76+
run: |
77+
python -m build
78+
79+
- name: Install package
80+
run: |
81+
pip install dist/*.whl
82+
83+
- name: Test installation
84+
run: |
85+
python -c "import diffgetr; print('Package imported successfully')"
86+
diffgetr --help || echo "CLI help command executed"
87+
88+
- name: Run unit tests
89+
run: |
90+
pytest tests/ -v || echo "No tests found yet"
91+
92+
format-code:
93+
runs-on: ubuntu-latest
94+
needs: build-and-test
95+
steps:
96+
- uses: actions/checkout@v4
97+
with:
98+
token: ${{ secrets.GITHUB_TOKEN }}
99+
ref: ${{ github.head_ref }}
100+
101+
- name: Set up Python
102+
uses: actions/setup-python@v4
103+
with:
104+
python-version: '3.9'
105+
106+
- name: Install black
107+
run: |
108+
python -m pip install --upgrade pip
109+
pip install black
110+
111+
- name: Format code with black
112+
run: |
113+
black --check --diff diffgetr/ || echo "Formatting needed"
114+
black diffgetr/
115+
116+
- name: Check for changes
117+
id: verify-changed-files
118+
run: |
119+
if [ -n "$(git status --porcelain)" ]; then
120+
echo "changed=true" >> $GITHUB_OUTPUT
121+
else
122+
echo "changed=false" >> $GITHUB_OUTPUT
123+
fi
124+
125+
- name: Commit formatted code
126+
if: steps.verify-changed-files.outputs.changed == 'true'
127+
run: |
128+
git config --local user.email "action@github.com"
129+
git config --local user.name "GitHub Action"
130+
git add -A
131+
git commit -m "🤖 Auto-format code with black"
132+
git push

CLAUDE.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
**diffgetr** is a Python library for comparing nested data structures with detailed diff reporting and interactive navigation. It provides advanced diff capabilities beyond basic comparison, featuring pattern recognition, multiple output formats, and dictionary-like navigation through diff results.
8+
9+
## Installation and Development Commands
10+
11+
```bash
12+
# Install the package locally
13+
pip install .
14+
15+
# Install in development mode
16+
pip install -e .
17+
18+
# Test the command line tool
19+
diffgetr file1.json file2.json path.to.key
20+
```
21+
22+
## Core Architecture
23+
24+
### Main Class: `diff_get`
25+
26+
The library centers around a single class `diff_get` located in `diffgetr/diff_get.py:9`. This class:
27+
28+
1. **Wraps DeepDiff**: Uses the `deepdiff` library as the underlying comparison engine with configurable parameters (`diffgetr/diff_get.py:22-24`)
29+
30+
2. **Enables Navigation**: Implements `__getitem__` to allow dictionary-like traversal through nested diff results (`diffgetr/diff_get.py:35-41`)
31+
32+
3. **Type Coercion**: Automatically converts lists/tuples to dictionaries for consistent comparison (`diffgetr/diff_get.py:14-17`)
33+
34+
4. **Location Tracking**: Maintains path context through the `loc` property for debugging and display (`diffgetr/diff_get.py:26-33`)
35+
36+
### Key Features
37+
38+
#### Interactive Navigation (`diffgetr/diff_get.py:35-57`)
39+
- Dictionary-style access: `diff['key1']['nested_key']`
40+
- IPython tab completion support via `_ipython_key_completions_`
41+
- Error handling with context-aware KeyError messages
42+
43+
#### Multiple Output Formats
44+
- **Summary**: Pattern recognition with frequency counts (`diff_summary()` at `diffgetr/diff_get.py:239-292`)
45+
- **Detailed**: Full diff with pretty printing (`diff_all()` at `diffgetr/diff_get.py:96-109`)
46+
- **Side-by-side**: Tabular comparison with percentage changes (`diff_sidebyside()` at `diffgetr/diff_get.py:111-237`)
47+
48+
#### Pattern Recognition (`diffgetr/diff_get.py:260-264`)
49+
- UUID detection and abstraction
50+
- CSV-like number sequence recognition
51+
- Path normalization for cleaner summaries
52+
53+
### Command Line Interface
54+
55+
Entry point defined in `pyproject.toml:29` as `diffgetr = "diffgetr.diff_get:main"`
56+
57+
The CLI (`main()` function at `diffgetr/diff_get.py:295-329`) supports:
58+
- JSON file comparison
59+
- Dot-notation path navigation
60+
- Array index navigation with bracket notation
61+
62+
### Configuration Options
63+
64+
#### DeepDiff Parameters (`diffgetr/diff_get.py:21-24`)
65+
Default settings:
66+
- `ignore_numeric_type_changes=True`
67+
- `significant_digits=3`
68+
69+
Can be overridden via `deep_diff_kw` parameter.
70+
71+
#### Behavior Modifiers
72+
- `ignore_added=False`: Filter out added items to focus on changes/removals
73+
- Configurable precision for numeric comparisons
74+
- Threshold-based filtering in side-by-side output
75+
76+
## Development Notes
77+
78+
### Code Structure
79+
- Single module design with one main class
80+
- Heavy use of property decorators for computed values
81+
- String/bytes handling for flexible output streams
82+
- Recursive instantiation for navigation
83+
84+
### Key Dependencies
85+
- `deepdiff>=6.0.0`: Core comparison engine
86+
- Standard library: `json`, `re`, `argparse`, `pprint`
87+
88+
### Testing Approach
89+
The library includes comprehensive examples in the README showing various use cases. When adding features, ensure compatibility with existing navigation patterns and output formats.

0 commit comments

Comments
 (0)