infinity-envguard 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Infinity™
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.
@@ -0,0 +1,482 @@
1
+ Metadata-Version: 2.4
2
+ Name: infinity-envguard
3
+ Version: 0.1.0
4
+ Summary: Local-first CLI that inspects environment-variable configuration and prevents common configuration mistakes.
5
+ Author: Infinity™
6
+ License-Expression: MIT
7
+ Keywords: env,environment,dotenv,config,secrets,cli
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Topic :: Software Development :: Quality Assurance
15
+ Classifier: Topic :: Utilities
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: typer>=0.12
20
+ Requires-Dist: rich>=13
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=8; extra == "dev"
23
+ Requires-Dist: ruff>=0.6; extra == "dev"
24
+ Requires-Dist: mypy>=1.10; extra == "dev"
25
+ Dynamic: license-file
26
+
27
+ # EnvGuard
28
+
29
+ **EnvGuard** is a local-first command-line tool that inspects your project's environment-variable configuration and prevents the configuration mistakes that break deploys and leak secrets.
30
+
31
+ It answers, without you grepping the entire codebase:
32
+
33
+ 1. Which environment variables does my code **require**, and which are missing?
34
+ 2. Which variables in my template are **stale or unused**?
35
+ 3. Is a real `.env` file **tracked by Git** — i.e. already in my history?
36
+ 4. Are any variables in the **`NEXT_PUBLIC_*`** namespace headed for the browser bundle?
37
+
38
+ Security and developer experience are treated as equal requirements: EnvGuard never prints, logs, or stores environment **values** — only names, counts, paths, and line numbers.
39
+
40
+ ## Status
41
+
42
+ **v0.1.0 — working local MVP.** All five commands below are implemented and covered by tests. EnvGuard is **not** published to any package index yet; it is installed from this repository (see Installation).
43
+
44
+ | Area | Status |
45
+ | --- | --- |
46
+ | License | MIT (see [LICENSE](LICENSE)) |
47
+
48
+ ## License
49
+
50
+ EnvGuard is released under the [MIT License](../envguard/LICENSE). See that file for the full license text and copyright notice.
51
+
52
+ > Infinite Toolbox is a monorepo containing multiple independent tools. Each tool may have its own license terms. The presence of this MIT license file applies to EnvGuard only and does not imply any particular license for other tools in the repository.
53
+
54
+ | Area | Status |
55
+ | --- | --- |
56
+ | `.env` / `.env.example` / variant discovery | ✅ implemented |
57
+ | Variable-name extraction (values never read into reports) | ✅ implemented |
58
+ | Python / JS / TS / Next.js reference detection | ✅ implemented |
59
+ | `check`, `init`, `sync`, `scan`, `doctor` | ✅ implemented |
60
+ | Git exposure classification (tracked / ignored / untracked / unknown) | ✅ implemented |
61
+ | CI-friendly exit codes + `--strict` | ✅ implemented |
62
+ | JSON output (`check --json`) | ✅ implemented |
63
+ | GitHub Actions CI workflow | ✅ implemented |
64
+ | Custom required-variable annotations, pre-commit hook | 🗓 planned |
65
+
66
+ ## Use cases
67
+
68
+ - **Before you commit:** catch a real `.env` file that Git is about to record forever.
69
+ - **Onboarding:** generate a `.env.example` from what the code actually reads.
70
+ - **CI gate:** fail the build when required variables are missing from the template or a secret-bearing file is tracked.
71
+ - **Auditing:** list every variable your code references and where — across Python, TypeScript, and Next.js in one pass.
72
+
73
+ ## Requirements
74
+
75
+ - Python **3.12+**
76
+ - **Git** (optional) — enables exposure checks; without a repository, EnvGuard reports states as `unknown` and never claims a file is safe
77
+
78
+ ## Installation (development stage)
79
+
80
+ ```bash
81
+ cd envguard
82
+ python3 -m venv .venv
83
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
84
+ pip install -e ".[dev]"
85
+ envguard --version
86
+ ```
87
+
88
+ To install the released package:
89
+
90
+ ```bash
91
+ python -m pip install infinity-envguard
92
+ ```
93
+
94
+ Package page: [infinity-envguard on PyPI](https://pypi.org/project/infinity-envguard/).
95
+ Repository: [erfmollaey/infinite-toolbox](https://github.com/erfmollaey/infinite-toolbox/tree/main/envguard).
96
+
97
+
98
+ Runtime dependencies are only **Typer** and **Rich**.
99
+
100
+ ## Security
101
+
102
+ - **Values are never printed** — not in reports, hints, exceptions, or debug output. The parser extracts names and counts non-empty values without ever handing values to the reporting layer.
103
+ - **Write operations never follow symlinks.** `sync --apply` and `init --force` reject symlinked targets.
104
+ - **Target validation.** `sync --apply` rejects targets that do not look like templates (`.env.example`, `.env.sample`, `.env.template`, `.env.dist`) to prevent accidentally modifying a real `.env` file.
105
+ - **JSON output** (`check --json`) emits structured data suitable for CI, with no secret values.
106
+
107
+ ## Commands
108
+
109
+ ### `envguard check [PATH]`
110
+
111
+ Detect missing variables and configuration inconsistencies.
112
+
113
+ ```console
114
+ $ envguard check
115
+ envguard check — /private/tmp/envguard-demo
116
+ File Kind Vars Notes
117
+ .env environment 2
118
+ .env.example template 2
119
+
120
+ Referenced variables (3):
121
+ DATABASE_URL (src/app.py)
122
+ JWT_SECRET (src/app.py)
123
+ NEXT_PUBLIC_API_URL public (web/client.ts)
124
+
125
+ Findings
126
+ ERROR 1 variable(s) referenced in code but not defined in .env:
127
+ NEXT_PUBLIC_API_URL
128
+ The application may fail at runtime. Add them locally (never commit the
129
+ real file) or provide them via your CI/host.
130
+ ERROR 2 variable(s) referenced in code but missing from .env.example:
131
+ JWT_SECRET, NEXT_PUBLIC_API_URL
132
+ Document each variable in the template, or remove the unused code
133
+ reference.
134
+ WARN 1 variable(s) in .env.example were not detected in source: UNUSED_VAR
135
+ Static analysis is best-effort: dynamic access (e.g. os.environ[name])
136
+ is not detected, so this can be a false positive. Verify manually before removing
137
+ anything.
138
+ INFO 1 NEXT_PUBLIC_* variable(s) are exposed to the browser bundle:
139
+ NEXT_PUBLIC_API_URL
140
+ Anything prefixed NEXT_PUBLIC_ is embedded at build time and readable
141
+ by end users. Never put secrets there.
142
+
143
+ Summary: 2 error(s), 1 warning(s), 1 info
144
+ ```
145
+
146
+ Exit code: `1` (errors found). Add `--strict` to also fail on warnings.
147
+
148
+ #### Machine-readable JSON output
149
+
150
+ Use `--json` to emit a machine-readable JSON document to stdout instead of the human-readable report:
151
+
152
+ ```bash
153
+ envguard check --json /path/to/project
154
+ ```
155
+
156
+ **Schema version:** `1.0.0`
157
+
158
+ **Example output:**
159
+
160
+ ```json
161
+ {
162
+ "schema_version": "1.0.0",
163
+ "tool": "envguard",
164
+ "version": "0.1.0",
165
+ "status": "error",
166
+ "summary": {
167
+ "errors": 1,
168
+ "warnings": 0,
169
+ "info": 0
170
+ },
171
+ "findings": [
172
+ {
173
+ "code": "missing-from-template",
174
+ "severity": "error",
175
+ "message": "1 variable(s) referenced in code but missing from .env.example: API_KEY",
176
+ "hint": "Document each variable in the template, or remove the unused code reference.",
177
+ "variables": ["API_KEY"]
178
+ }
179
+ ],
180
+ "referenced_variables": [
181
+ {"name": "API_KEY", "files": ["src/app.py"]}
182
+ ],
183
+ "env_files": [
184
+ {"path": ".env", "kind": "environment", "variables": 1, "notes": [""]},
185
+ {"path": ".env.example", "kind": "template", "variables": 0, "notes": [""]}
186
+ ],
187
+ "skipped_sources": []
188
+ }
189
+ ```
190
+
191
+ **JSON fields:**
192
+
193
+ | Field | Type | Description |
194
+ |-------|------|-------------|
195
+ | `schema_version` | string | Schema version for future evolution |
196
+ | `tool` | string | Always "envguard" |
197
+ | `version` | string | EnvGuard version |
198
+ | `status` | string | "ok", "warning", or "error" |
199
+ | `summary` | object | Counts by severity |
200
+ | `findings` | array | Finding objects with `code`, `severity`, `message`, optional `hint`, `file`, `variables` |
201
+ | `referenced_variables` | array | Variables referenced in code with source files |
202
+ | `env_files` | array | Discovered environment files |
203
+ | `skipped_sources` | array | Source files that could not be read |
204
+
205
+ **Important:** JSON output never contains environment variable values, absolute paths, or secret data. Exit codes are identical to human-readable mode.
206
+
207
+ **Finding codes are stable and documented:**
208
+
209
+ | Code | Severity | Description |
210
+ |------|----------|-------------|
211
+ | `missing-from-template` | error | Variable referenced in code but not in the template |
212
+ | `missing-from-env` | error | Variable referenced in code but not in the real `.env` file |
213
+ | `unused-in-template` | warning | Variable in template with no code reference (may be intentional) |
214
+ | `nextpublic` | error | `NEXT_PUBLIC_*` variable found in a real `.env` file |
215
+ | `public-vars` | info | `NEXT_PUBLIC_*` variables found anywhere |
216
+ | `no-template` | warning | No template file found |
217
+ | `no-real-env` | warning | No real `.env` file found |
218
+ | `env-file-not-ignored` | warning | Real `.env` not in `.gitignore` |
219
+ | `env-file-tracked` | error | Real `.env` tracked by Git |
220
+ | `template-populated` | warning | Template contains non-empty values |
221
+
222
+ ### `envguard init [PATH]`
223
+
224
+ Create a sample environment file from safely discovered variable names (source references ∪ existing environment files). Values are always written empty.
225
+
226
+ ```console
227
+ $ envguard init
228
+ envguard init — /private/tmp/envguard-init
229
+ Created .env.example with 2 name(s):
230
+ API_KEY=
231
+ DB_URL=
232
+ Values are intentionally left empty — fill them in locally and never commit a
233
+ real .env file.
234
+ ```
235
+
236
+ Refuses to overwrite an existing file unless `--force` is given (exit `2`).
237
+ `--output/-o` chooses a different file name.
238
+
239
+ ### `envguard sync [PATH]`
240
+
241
+ Compare a real environment file against a template and propose safe synchronization. **Dry-run by default** — nothing is written until you pass `--apply`, the source file is never modified, and no line is ever removed from the target.
242
+
243
+ ```console
244
+ $ envguard sync
245
+ envguard sync — /private/tmp/envguard-demo
246
+ Compared .env (2 names) → .env.example (2 names)
247
+ only in .env.example (1): UNUSED_VAR
248
+
249
+ Proposed additions to .env.example (1):
250
+ JWT_SECRET=
251
+
252
+ Dry run: no file was modified. Re-run with --apply to append these names.
253
+ ```
254
+
255
+ ```console
256
+ $ envguard sync --apply
257
+ ...
258
+ Appended 1 name(s) to .env.example.
259
+ The source file was not modified.
260
+ ```
261
+
262
+ Exit codes: `1` when differences exist in dry-run mode, `0` after a successful `--apply` or when already in sync. `--source` / `--target` select the two files.
263
+
264
+ ### `envguard scan [PATH]`
265
+
266
+ Inspect environment files and Git configuration for potential secret exposure.
267
+
268
+ ```console
269
+ $ envguard scan
270
+ envguard scan — /private/tmp/envguard-demo
271
+ File Kind Vars Notes
272
+ .env environment 2
273
+ .env.example template 2
274
+
275
+ Git exposure
276
+ .env: tracked — committed to Git history
277
+ .env.example: untracked — exists locally but is not ignored
278
+
279
+ Findings
280
+ ERROR .env is tracked by Git. A real environment file must never be committed.
281
+ Remove it from the index (`git rm --cached <file>`), rotate every
282
+ secret it contained, and add it to .gitignore.
283
+
284
+ Summary: 1 error(s), 0 warning(s), 0 info
285
+ ```
286
+
287
+ Exit code: `1` when a real environment file is tracked. `--no-git` skips Git checks; `--strict` also fails on warnings.
288
+
289
+ ### `envguard doctor [PATH]`
290
+
291
+ Concise overall configuration-health report: environment files, Git exposure, all findings from `check` + `scan`, and a one-line verdict.
292
+
293
+ ```console
294
+ $ envguard doctor
295
+ ...
296
+ Summary: 3 error(s), 1 warning(s), 1 info
297
+ Verdict: UNSAFE configuration — fix the errors above.
298
+ ```
299
+
300
+ ## Exit codes
301
+
302
+ | Code | Meaning |
303
+ | --- | --- |
304
+ | `0` | Success, no blocking findings (warnings allowed unless `--strict`) |
305
+ | `1` | Findings reported: missing variables, tracked real `.env`, or `sync` differences in dry-run |
306
+ | `2` | Operational error: bad path, missing/unreadable file, refusal to overwrite without `--force` |
307
+ | `2` | Invoking `envguard` with no subcommand (help is printed) |
308
+
309
+ ## CI integration
310
+
311
+ ### GitHub Actions example
312
+
313
+ The repository includes a GitHub Actions workflow (`.github/workflows/envguard.yml`) that runs EnvGuard's own quality checks. To use EnvGuard to check **another project's** environment configuration in CI:
314
+
315
+ ```yaml
316
+ # In your project's .github/workflows/env-check.yml
317
+ name: Environment check
318
+
319
+ on:
320
+ pull_request:
321
+ push:
322
+ branches: [main]
323
+
324
+ jobs:
325
+ envguard:
326
+ runs-on: ubuntu-latest
327
+ steps:
328
+ - uses: actions/checkout@v4
329
+
330
+ - name: Set up Python
331
+ uses: actions/setup-python@v5
332
+ with:
333
+ python-version: "3.12"
334
+
335
+ - name: Install EnvGuard
336
+ run: pip install infinity-envguard
337
+
338
+ - name: Check environment configuration
339
+ run: envguard check --strict
340
+ # Or for JSON output suitable for programmatic processing:
341
+ # run: envguard check --json --strict
342
+ ```
343
+
344
+ **Important distinction:**
345
+
346
+ - **EnvGuard's own CI** (`.github/workflows/envguard.yml`): Runs EnvGuard's test suite, linting, formatting, type checking, and build validation. This validates EnvGuard itself.
347
+ - **Using EnvGuard in your project's CI**: Installs EnvGuard as a tool and runs it against your project's source code to validate your environment configuration. This validates your project.
348
+
349
+ ### Exit codes for CI
350
+
351
+ | Code | Meaning | CI action |
352
+ |------|----------|-----------|
353
+ | `0` | Success, no findings | Pass |
354
+ | `1` | Findings found (errors or warnings with `--strict`) | Fail |
355
+ | `2` | Operational error (bad path, missing file, etc.) | Fail |
356
+
357
+ Use `--strict` to treat warnings as failures in CI:
358
+
359
+ ```bash
360
+ envguard check --strict # Fail on both errors and warnings
361
+ envguard check --json --strict # JSON output, fail on warnings
362
+ ```
363
+
364
+ ## Supported detection patterns
365
+
366
+ | Language | Patterns |
367
+ | --- | --- |
368
+ | Python | `os.getenv("X")`, `getenv("X")`, `os.environ["X"]`, `os.environ.get("X")`, `os.environ.setdefault("X", ...)` |
369
+ | JavaScript / TypeScript | `process.env.X`, `process.env["X"]` |
370
+ | Next.js / Vite | `process.env.NEXT_PUBLIC_X`, `import.meta.env.X` |
371
+
372
+ Environment-file discovery covers `.env`, `.env.local`, `.env.example`, `.env.sample`, `.env.template`, `.env.development`, `.env.production`, and other `.env.*` variants — matched **by file name only**, so unrelated files are never opened.
373
+
374
+ ## Security guarantees
375
+
376
+ - **Values are never printed** — not in reports, hints, exceptions, or debug output. The parser extracts names and counts non-empty values without ever handing values to the reporting layer.
377
+ - **Values never enter test snapshots or assertion messages.** Test fixtures use obvious placeholders (`sk-test-DO-NOT-LEAK-12345`) and the suite asserts these never appear in CLI output.
378
+ - **Nothing leaves your machine.** No network access, no telemetry, no persistence outside your project.
379
+ - **Write operations never follow symlinks.** `sync --apply` and `init --force` reject symlinked targets, dangling symlinks, and non-regular files (FIFOs, directories). When Git is available, `.gitignore` is respected exactly via `git ls-files`; in the non-Git fallback walk, `.gitignore` is **not** applied. Symlinked directories are never traversed during discovery.
380
+ - **Environment-file discovery is bounded** to 4 directory levels below the project root (`ENV_FILE_MAX_DEPTH = 4`); deeper `.env` variants are not found.
381
+ - **Non-destructive by default.** `check`, `scan`, and `doctor` never write; `sync` requires `--apply`; `init` refuses to overwrite without `--force`; Git history is never modified and nothing is ever staged or committed.
382
+ - **Failure is never reported as safety.** When Git cannot answer (no repository, permission error, Git missing), the state is `unknown` with the reason shown — never "safe" or "untracked".
383
+ - **`NEXT_PUBLIC_*` is always flagged** as browser-exposed configuration, regardless of where it appears.
384
+ - **Target validation.** `sync --apply` rejects targets that do not look like templates (`.env.example`, `.env.sample`, `.env.template`, `.env.dist`) to prevent accidentally modifying a real `.env` file. Use `--allow-non-template` only when you intentionally target a non-template file.
385
+
386
+ ## Limitations
387
+
388
+ - Detection is **best-effort static analysis**: literal references are found; dynamic access such as `os.environ[name]` or `process.env[key]` is not. `unused-in-template` warnings can therefore be false positives — verify before removing variables.
389
+ - "Missing from `.env`" is evaluated against the file on disk, not the runtime environment of a deployed service.
390
+ - Multi-line quoted values in environment files are not parsed as continuations (the first line's name is still captured; following lines are reported as malformed).
391
+ - Git checks cover the repository you run EnvGuard in; worktrees/submodules are inspected as their own repositories.
392
+ - The scanner is best-effort detection, **not a guarantee** that all secrets or all configuration problems have been found.
393
+ - Environment-file discovery is limited to 4 directory levels below the project root. Deeper `.env` variants are not found.
394
+ - In non-Git projects, the fallback source walk does not apply `.gitignore`; files that would be ignored in a Git repository may still be scanned.
395
+
396
+ ## CI integration
397
+
398
+ ### GitHub Actions example
399
+
400
+ The repository includes a GitHub Actions workflow (`.github/workflows/envguard.yml`) that runs EnvGuard's own quality checks. To use EnvGuard to check **another project's** environment configuration in CI:
401
+
402
+ ```yaml
403
+ # In your project's .github/workflows/env-check.yml
404
+ name: Environment check
405
+
406
+ on:
407
+ pull_request:
408
+ push:
409
+ branches: [main]
410
+
411
+ jobs:
412
+ envguard:
413
+ runs-on: ubuntu-latest
414
+ steps:
415
+ - uses: actions/checkout@v4
416
+
417
+ - name: Set up Python
418
+ uses: actions/setup-python@v5
419
+ with:
420
+ python-version: "3.12"
421
+
422
+ - name: Install EnvGuard
423
+ run: pip install infinity-envguard
424
+
425
+ - name: Check environment configuration
426
+ run: envguard check --strict
427
+ # Or for JSON output suitable for programmatic processing:
428
+ # run: envguard check --json --strict
429
+ ```
430
+
431
+ **Important distinction:**
432
+
433
+ - **EnvGuard's own CI** (`.github/workflows/envguard.yml`): Runs EnvGuard's test suite, linting, formatting, type checking, and build validation. This validates EnvGuard itself.
434
+ - **Using EnvGuard in your project's CI**: Installs EnvGuard as a tool and runs it against your project's source code to validate your environment configuration. This validates your project.
435
+
436
+ ### Exit codes for CI
437
+
438
+ | Code | Meaning | CI action |
439
+ |------|----------|-----------|
440
+ | `0` | Success, no findings | Pass |
441
+ | `1` | Findings found (errors or warnings with `--strict`) | Fail |
442
+ | `2` | Operational error (bad path, missing file, etc.) | Fail |
443
+
444
+ Use `--strict` to treat warnings as failures in CI:
445
+
446
+ ```bash
447
+ envguard check --strict # Fail on both errors and warnings
448
+ envguard check --json --strict # JSON output, fail on warnings
449
+ ```
450
+
451
+ ## Development
452
+
453
+ ```bash
454
+ cd envguard
455
+ source .venv/bin/activate
456
+
457
+ pytest # test suite (55 tests)
458
+ ruff check src tests # lint
459
+ ruff format src tests # format
460
+ mypy src tests # strict type checking
461
+ ```
462
+
463
+ Module layout — parsing/analysis are deliberately separate from presentation:
464
+
465
+ ```text
466
+ src/envguard/
467
+ ├── discovery.py # safe filesystem discovery (no symlinks, no irrelevant dirs)
468
+ ├── envfiles.py # env-file parsing → names only, never values
469
+ ├── sources.py # reference detection (Python/JS/TS/Next.js patterns)
470
+ ├── analysis.py # pure comparison logic → findings (no I/O, no output)
471
+ ├── gitcheck.py # tracked / ignored / untracked / unknown classification
472
+ ├── report.py # Rich rendering (receives names & counts only)
473
+ └── cli.py # Typer commands, exit codes
474
+ ```
475
+
476
+ ## Future improvements
477
+
478
+ - Machine-readable `--json` output for CI tooling
479
+ - Optional per-variable annotations (e.g. required vs optional)
480
+ - Pre-commit hook and CI workflow examples
481
+ - Additional language patterns (Go `os.Getenv`, Rust `std::env::var`, shell `${VAR}`)
482
+ - Multi-line value-aware parsing for environment files