envdrift 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
envdrift/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ """Prevent environment variable drift with Pydantic schema validation.
2
+
3
+ envdrift helps you:
4
+ - Validate .env files against Pydantic schemas
5
+ - Detect drift between environments (dev, staging, prod)
6
+ - Integrate with pre-commit hooks and CI/CD pipelines
7
+ - Support dotenvx encryption for secure .env files
8
+ """
9
+
10
+ __version__ = "0.0.1"
11
+ __author__ = "Jainal Gosaliya"
12
+ __email__ = "gosaliya.jainal@gmail.com"
13
+
14
+ from envdrift.core import validate, diff, init
15
+
16
+ __all__ = ["validate", "diff", "init", "__version__"]
envdrift/cli.py ADDED
@@ -0,0 +1,107 @@
1
+ """Command-line interface for envdrift."""
2
+
3
+ from pathlib import Path
4
+ from typing import Annotated, Optional
5
+
6
+ import typer
7
+ from rich.console import Console
8
+ from rich.panel import Panel
9
+
10
+ app = typer.Typer(
11
+ name="envdrift",
12
+ help="Prevent environment variable drift with Pydantic schema validation.",
13
+ no_args_is_help=True,
14
+ )
15
+ console = Console()
16
+
17
+
18
+ @app.command()
19
+ def validate(
20
+ env_file: Annotated[
21
+ Path, typer.Argument(help="Path to .env file to validate")
22
+ ] = Path(".env"),
23
+ schema: Annotated[
24
+ Optional[str],
25
+ typer.Option("--schema", "-s", help="Dotted path to Settings class"),
26
+ ] = None,
27
+ ci: Annotated[
28
+ bool, typer.Option("--ci", help="CI mode: exit with code 1 on failure")
29
+ ] = False,
30
+ ) -> None:
31
+ """Validate an .env file against a Pydantic schema."""
32
+ console.print(
33
+ Panel(
34
+ "[yellow]Coming soon in v0.1.0[/yellow]\n\n"
35
+ "This command will validate your .env file against a Pydantic Settings schema.",
36
+ title="envdrift validate",
37
+ )
38
+ )
39
+ if ci:
40
+ raise typer.Exit(code=1)
41
+
42
+
43
+ @app.command()
44
+ def diff(
45
+ env1: Annotated[Path, typer.Argument(help="First .env file (e.g., .env.dev)")],
46
+ env2: Annotated[Path, typer.Argument(help="Second .env file (e.g., .env.prod)")],
47
+ ) -> None:
48
+ """Compare two .env files and show differences."""
49
+ console.print(
50
+ Panel(
51
+ "[yellow]Coming soon in v0.1.0[/yellow]\n\n"
52
+ f"This command will compare [bold]{env1}[/bold] and [bold]{env2}[/bold]\n"
53
+ "and show missing, extra, and differing variables.",
54
+ title="envdrift diff",
55
+ )
56
+ )
57
+
58
+
59
+ @app.command()
60
+ def init(
61
+ env_file: Annotated[
62
+ Path, typer.Argument(help="Path to .env file to generate schema from")
63
+ ] = Path(".env"),
64
+ output: Annotated[
65
+ Path, typer.Option("--output", "-o", help="Output file for Settings class")
66
+ ] = Path("settings.py"),
67
+ ) -> None:
68
+ """Generate a Pydantic Settings class from an existing .env file."""
69
+ console.print(
70
+ Panel(
71
+ "[yellow]Coming soon in v0.1.0[/yellow]\n\n"
72
+ f"This command will generate a Pydantic Settings class\n"
73
+ f"from [bold]{env_file}[/bold] and write it to [bold]{output}[/bold].",
74
+ title="envdrift init",
75
+ )
76
+ )
77
+
78
+
79
+ @app.command()
80
+ def hook(
81
+ install: Annotated[
82
+ bool, typer.Option("--install", "-i", help="Install pre-commit hook")
83
+ ] = False,
84
+ ) -> None:
85
+ """Manage pre-commit hook integration."""
86
+ if install:
87
+ console.print(
88
+ Panel(
89
+ "[yellow]Coming soon in v0.1.0[/yellow]\n\n"
90
+ "This command will add envdrift to your .pre-commit-config.yaml",
91
+ title="envdrift hook",
92
+ )
93
+ )
94
+ else:
95
+ console.print("Use --install to add envdrift pre-commit hook")
96
+
97
+
98
+ @app.command()
99
+ def version() -> None:
100
+ """Show envdrift version."""
101
+ from envdrift import __version__
102
+
103
+ console.print(f"envdrift [bold green]{__version__}[/bold green]")
104
+
105
+
106
+ if __name__ == "__main__":
107
+ app()
envdrift/core.py ADDED
@@ -0,0 +1,47 @@
1
+ """Core functionality for envdrift."""
2
+
3
+ from pathlib import Path
4
+
5
+
6
+ def validate(env_file: Path | str = ".env", schema: str | None = None) -> bool:
7
+ """Validate an .env file against a Pydantic schema.
8
+
9
+ Args:
10
+ env_file: Path to the .env file to validate
11
+ schema: Dotted path to the Pydantic Settings class (e.g., 'app.config:Settings')
12
+
13
+ Returns:
14
+ True if validation passes, False otherwise
15
+
16
+ Raises:
17
+ NotImplementedError: This feature is coming soon
18
+ """
19
+ raise NotImplementedError("Coming soon in v0.1.0")
20
+
21
+
22
+ def diff(env1: Path | str, env2: Path | str) -> dict[str, tuple[str | None, str | None]]:
23
+ """Compare two .env files and return differences.
24
+
25
+ Args:
26
+ env1: Path to first .env file
27
+ env2: Path to second .env file
28
+
29
+ Returns:
30
+ Dictionary of differences: {key: (value_in_env1, value_in_env2)}
31
+
32
+ Raises:
33
+ NotImplementedError: This feature is coming soon
34
+ """
35
+ raise NotImplementedError("Coming soon in v0.1.0")
36
+
37
+
38
+ def init(output: Path | str = "settings.py") -> None:
39
+ """Generate a Pydantic Settings class from an existing .env file.
40
+
41
+ Args:
42
+ output: Path where to write the generated Settings class
43
+
44
+ Raises:
45
+ NotImplementedError: This feature is coming soon
46
+ """
47
+ raise NotImplementedError("Coming soon in v0.1.0")
envdrift/py.typed ADDED
File without changes
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: envdrift
3
+ Version: 0.0.1
4
+ Summary: Prevent environment variable drift with Pydantic schema validation, pre-commit hooks, and dotenvx encryption
5
+ Project-URL: Homepage, https://github.com/jainal09/envdrift
6
+ Project-URL: Documentation, https://github.com/jainal09/envdrift#readme
7
+ Project-URL: Repository, https://github.com/jainal09/envdrift
8
+ Project-URL: Issues, https://github.com/jainal09/envdrift/issues
9
+ Author-email: Jainal Gosaliya <gosaliya.jainal@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: config,dotenv,drift,environment,pre-commit,pydantic,schema,secrets,validation,variables
13
+ Classifier: Development Status :: 1 - Planning
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Security
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: Software Development :: Quality Assurance
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.11
26
+ Requires-Dist: pydantic-settings>=2.0
27
+ Requires-Dist: pydantic>=2.0
28
+ Requires-Dist: python-dotenv>=1.0
29
+ Requires-Dist: rich>=13.0
30
+ Requires-Dist: typer>=0.9
31
+ Provides-Extra: dev
32
+ Requires-Dist: bandit>=1.7.0; extra == 'dev'
33
+ Requires-Dist: pre-commit>=3.0; extra == 'dev'
34
+ Requires-Dist: pyrefly>=0.2.0; extra == 'dev'
35
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
36
+ Requires-Dist: pytest>=8.0; extra == 'dev'
37
+ Requires-Dist: ruff>=0.8.0; extra == 'dev'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # envdrift
41
+
42
+ [![PyPI version](https://badge.fury.io/py/envdrift.svg)](https://badge.fury.io/py/envdrift)
43
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
44
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
45
+
46
+ **Prevent environment variable drift between dev, staging, and production.**
47
+
48
+ > 🚧 **Under Active Development** - Core features coming in v0.1.0
49
+
50
+ ## The Problem
51
+
52
+ Environment variable drift is a silent killer of deployments:
53
+
54
+ - A missing `DATABASE_URL` in production causes a 3am outage
55
+ - Staging has `NEW_FEATURE_FLAG=true` but production doesn't
56
+ - Someone copies the wrong `.env` file and chaos ensues
57
+ - "It works on my machine!" becomes your team's motto
58
+
59
+ **In 2024 alone, 24 million secrets were leaked on GitHub.** Knight Capital lost **$460 million in 45 minutes** due to a configuration deployment error.
60
+
61
+ ## The Solution
62
+
63
+ `envdrift` treats your environment variables with the same rigor as your code:
64
+
65
+ - **Schema Validation**: Define expected variables with Pydantic, catch mismatches at startup
66
+ - **Drift Detection**: Compare `.env.dev` vs `.env.prod` and see exactly what differs
67
+ - **Pre-commit Hooks**: Block commits if your `.env` doesn't match your schema
68
+ - **CI/CD Integration**: Fail fast in pipelines before bad config reaches production
69
+ - **Encryption Support**: Works with dotenvx for secure, committable `.env` files
70
+
71
+ ## Installation
72
+
73
+ ```bash
74
+ pip install envdrift
75
+ # or
76
+ uv add envdrift
77
+ ```
78
+
79
+ ## Quick Start
80
+
81
+ ### Validate your .env against a schema
82
+
83
+ ```bash
84
+ envdrift validate .env --schema myapp.config:Settings
85
+ ```
86
+
87
+ ### Compare environments
88
+
89
+ ```bash
90
+ envdrift diff .env.dev .env.prod
91
+ ```
92
+
93
+ ### Generate a Settings class from existing .env
94
+
95
+ ```bash
96
+ envdrift init .env --output settings.py
97
+ ```
98
+
99
+ ### Install pre-commit hook
100
+
101
+ ```bash
102
+ envdrift hook --install
103
+ ```
104
+
105
+ ## Planned Features (v0.1.0)
106
+
107
+ - [ ] `envdrift validate` - Validate .env against Pydantic schema
108
+ - [ ] `envdrift diff` - Compare two .env files
109
+ - [ ] `envdrift init` - Generate Settings class from .env
110
+ - [ ] `envdrift hook` - Pre-commit hook integration
111
+ - [ ] Rich terminal output with clear error messages
112
+ - [ ] dotenvx encryption detection and support
113
+ - [ ] CI mode with proper exit codes
114
+
115
+ ## Why envdrift?
116
+
117
+ | Feature | python-dotenv | dynaconf | pydantic-settings | **envdrift** |
118
+ |---------|---------------|----------|-------------------|--------------|
119
+ | Load .env | ✅ | ✅ | ✅ | ✅ |
120
+ | Type validation | ❌ | ⚠️ | ✅ | ✅ |
121
+ | Schema enforcement | ❌ | ⚠️ | ✅ | ✅ |
122
+ | Cross-env diff | ❌ | ❌ | ❌ | ✅ |
123
+ | Pre-commit hook | ❌ | ❌ | ❌ | ✅ |
124
+ | Encryption support | ❌ | ❌ | ❌ | ✅ |
125
+
126
+ ## Development
127
+
128
+ ```bash
129
+ # Clone the repo
130
+ git clone https://github.com/jainal09/envdrift.git
131
+ cd envdrift
132
+
133
+ # Install dev dependencies
134
+ make dev
135
+
136
+ # Run checks
137
+ make check
138
+ ```
139
+
140
+ ## License
141
+
142
+ MIT License - see [LICENSE](LICENSE) for details.
143
+
144
+ ## Author
145
+
146
+ **Jainal Gosaliya** - [gosaliya.jainal@gmail.com](mailto:gosaliya.jainal@gmail.com)
@@ -0,0 +1,9 @@
1
+ envdrift/__init__.py,sha256=ZUscFngMxW1DbYNUrDRO34GqfE7od57Yh-YK1pALb2A,502
2
+ envdrift/cli.py,sha256=DzhRpLecdY_0V92BdfNLYR0yZKUT8d9yhJKAmwfmnnA,3095
3
+ envdrift/core.py,sha256=dkFn6bkJNXPdVr8AGHicHwB36wE_uNgf1YN2uCnHlAM,1342
4
+ envdrift/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ envdrift-0.0.1.dist-info/METADATA,sha256=K3aK7sOYrxv1EVhEPDzObwDFqC8nr9Mx217YT0NfRi8,4773
6
+ envdrift-0.0.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
7
+ envdrift-0.0.1.dist-info/entry_points.txt,sha256=njqKNXn7qzidlqaoWgy8_Cwc8F_Sc2Jle2tyPUsMJDU,46
8
+ envdrift-0.0.1.dist-info/licenses/LICENSE,sha256=alWX8zV_Hd76Ze1Wasp1gVkcYypBD9VY5S8uqQhStQw,1072
9
+ envdrift-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ envdrift = envdrift.cli:app
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jainal Gosaliya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.