envshield 4.3.0__tar.gz → 4.4.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.
- {envshield-4.3.0 → envshield-4.4.0}/PKG-INFO +72 -9
- {envshield-4.3.0 → envshield-4.4.0}/README.md +70 -7
- envshield-4.4.0/envshield/__init__.py +1 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/cli.py +177 -51
- {envshield-4.3.0 → envshield-4.4.0}/envshield/config/manager.py +25 -11
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/doctor.py +49 -11
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/exceptions.py +1 -5
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/generator.py +25 -12
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/hooks_manager.py +4 -4
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/importer.py +7 -8
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/inspector.py +3 -2
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/scanner.py +86 -24
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/schema_manager.py +72 -9
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/schema_types.py +1 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/service_discovery.py +15 -4
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/setup_manager.py +18 -13
- {envshield-4.3.0 → envshield-4.4.0}/envshield/parsers/_base.py +1 -1
- {envshield-4.3.0 → envshield-4.4.0}/envshield/parsers/_docker_compose.py +5 -5
- {envshield-4.3.0 → envshield-4.4.0}/envshield/parsers/_dotenv.py +2 -1
- {envshield-4.3.0 → envshield-4.4.0}/envshield/parsers/_kubernetes.py +4 -3
- {envshield-4.3.0 → envshield-4.4.0}/envshield/parsers/_python.py +1 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/core/test_doctor.py +6 -11
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/core/test_generator.py +10 -4
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/core/test_importer.py +6 -15
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/core/test_scanner_compliance.py +8 -20
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/core/test_schema_manager.py +28 -44
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/core/test_schema_types.py +8 -3
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/core/test_service_discovery.py +14 -7
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/core/test_service_manager.py +10 -8
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/core/test_setup_manager.py +13 -21
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/parsers/test_docker_compose_parser.py +9 -22
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/parsers/test_kubernetes_parser.py +4 -10
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/parsers/test_python_parser.py +1 -9
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/test_cli.py +169 -16
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/test_config_manager.py +36 -37
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/test_diff_aware_scanning.py +23 -11
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/test_git_utils.py +15 -5
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/test_service_cli.py +8 -2
- {envshield-4.3.0 → envshield-4.4.0}/envshield.egg-info/PKG-INFO +72 -9
- {envshield-4.3.0 → envshield-4.4.0}/envshield.egg-info/requires.txt +1 -1
- {envshield-4.3.0 → envshield-4.4.0}/pyproject.toml +19 -2
- envshield-4.3.0/envshield/__init__.py +0 -1
- {envshield-4.3.0 → envshield-4.4.0}/LICENSE.md +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/__main__.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/config/__init.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/__init__.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/file_updater.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/core/service_manager.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/parsers/__init__.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/parsers/_deployment.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/parsers/factory.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/state.py +1 -1
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/__init__.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/conftest.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/core/__init__.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/core/test_file_updater.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/parsers/__init__.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/tests/parsers/test_dotenv_parser.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/utils/__init__.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield/utils/git_utils.py +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield.egg-info/SOURCES.txt +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield.egg-info/dependency_links.txt +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield.egg-info/entry_points.txt +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/envshield.egg-info/top_level.txt +0 -0
- {envshield-4.3.0 → envshield-4.4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: envshield
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.4.0
|
|
4
4
|
Summary: EnvShield: A CLI for secure local environment management and secret prevention.
|
|
5
5
|
Author-email: Rabbil Yasar Sajal <rabbilyasar@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -47,7 +47,7 @@ Requires-Dist: questionary<3.0,>=2.0
|
|
|
47
47
|
Requires-Dist: typer[all]<1.0,>=0.9
|
|
48
48
|
Requires-Dist: toml<1.0,>=0.10
|
|
49
49
|
Provides-Extra: dev
|
|
50
|
-
Requires-Dist: ruff; extra == "dev"
|
|
50
|
+
Requires-Dist: ruff<0.17,>=0.16; extra == "dev"
|
|
51
51
|
Requires-Dist: pytest; extra == "dev"
|
|
52
52
|
Requires-Dist: pytest-mock; extra == "dev"
|
|
53
53
|
Requires-Dist: bump2version; extra == "dev"
|
|
@@ -92,6 +92,7 @@ It works the same way whether you have one repo with one `.env` file, or a monor
|
|
|
92
92
|
- [Command reference](#command-reference)
|
|
93
93
|
- [Core commands](#core-commands)
|
|
94
94
|
- [Monorepo: managing multiple services (monorepo)](#monorepo-managing-multiple-services)
|
|
95
|
+
- [Machine-readable output (`--json`)](#machine-readable-output---json)
|
|
95
96
|
- [Typed config code generation](#typed-config-code-generation)
|
|
96
97
|
- [Validating deployment manifests](#validating-deployment-manifests)
|
|
97
98
|
- [Secret scanning and git hooks](#secret-scanning-and-git-hooks)
|
|
@@ -360,12 +361,12 @@ Everything a single-service project ever needs. `--service` shows up on most of
|
|
|
360
361
|
|---|---|
|
|
361
362
|
| `envshield init [--force/-f]` | Detects your framework and builds `env.schema.toml` from a real config source if it finds one, otherwise a framework-aware template. Also scaffolds `envshield.yml`, updates `.gitignore`, and offers to install git hooks. Auto-registers a root-level `docker-compose.yml` as the project's deployment manifest if it finds one. `--force` re-runs on a project that already has a config (with a confirmation before overwriting). |
|
|
362
363
|
| `envshield import <file> [--output/-o PATH] [--force/-f] [--interactive] [--service NAME]` | Runs the same real-variable analysis `init` does automatically, as its own command — for re-importing after your code gains new variables, pointing at a file `init` wouldn't have found, or adding `--interactive` to confirm each secret/type classification by hand instead of accepting the automatic guess. `--output` changes where the schema is written (defaults to `env.schema.toml`, or the target service's schema path with `--service`). |
|
|
363
|
-
| `envshield check [file] [--service NAME] [--container NAME]` | Validates a local file (or, if omitted, the project's/service's default local file *and* its registered deployment manifest, if any) against the schema. `file` can be a plain `.env`, a Python config module, a docker-compose file, or a Kubernetes manifest. `--container` picks which service/container to check in a manifest that declares more than one (tried against `--service`'s name automatically first). Exits non-zero on any drift — safe to use as a CI gate. |
|
|
364
|
-
| `envshield doctor [--fix] [--service NAME]` | Runs every health check at once (see below) and reports a summary. `--fix` interactively offers to fix whatever it can — re-running `init`, regenerating the template, installing the git hook, or running `setup` to fill in missing/invalid local values. Exits non-zero if anything's still broken afterward. |
|
|
364
|
+
| `envshield check [file] [--service NAME] [--container NAME] [--json]` | Validates a local file (or, if omitted, the project's/service's default local file *and* its registered deployment manifest, if any) against the schema. `file` can be a plain `.env`, a Python config module, a docker-compose file, or a Kubernetes manifest. `--container` picks which service/container to check in a manifest that declares more than one (tried against `--service`'s name automatically first). Exits non-zero on any drift — safe to use as a CI gate. `--json` prints a machine-readable result instead (see below) and never falls into an interactive service picker. |
|
|
365
|
+
| `envshield doctor [--fix] [--service NAME] [--json]` | Runs every health check at once (see below) and reports a summary. `--fix` interactively offers to fix whatever it can — re-running `init`, regenerating the template, installing the git hook, or running `setup` to fill in missing/invalid local values. Exits non-zero if anything's still broken afterward. `--json` is incompatible with `--fix` (an interactive confirm prompt makes no sense in a machine-readable mode). |
|
|
365
366
|
| `envshield setup [output_file] [--service NAME]` | Interactive onboarding wizard: walks through every variable that's missing, blank, or has an existing value the schema no longer allows, prompting with the variable's description, masking secret input, and offering a picker for `enum` fields. Leaves everything already correct untouched. |
|
|
366
367
|
| `envshield schema sync [--service NAME]` | Regenerates `.env.example` from the schema (a dotenv project), or patches a Python-module local file in place to declare any schema variable it's missing (never rewrites it wholesale — only appends/patches the specific lines it owns). `import` already calls this automatically for you when it changes a project's/service's real schema, so you'll rarely need to run it by hand except after a manual schema edit. |
|
|
367
|
-
| `envshield generate [output_file] [--lang/-l python\|typescript] [--force/-f] [--service NAME]` | Compiles the schema into a typed, validated config module. `--lang` is auto-detected from your project (Next.js/Vite/Node.js → TypeScript,
|
|
368
|
-
| `envshield scan [paths...] [--staged] [--config/-c PATH] [--exclude/-e PATTERN]
|
|
368
|
+
| `envshield generate [output_file] [--lang/-l python\|typescript] [--force/-f] [--service NAME]` | Compiles the schema into a typed, validated config module. `--lang` is auto-detected from your project (Next.js/Vite/Node.js → TypeScript; Python/Django/Flask, or nothing detected → Python) if omitted. A detected ecosystem with no codegen target at all (currently: Go) errors and asks for `--lang` explicitly, rather than silently guessing Python. Defaults to writing `config.py`/`config.ts`; `--force` overwrites an existing output file. See [Typed config code generation](#typed-config-code-generation). |
|
|
369
|
+
| `envshield scan [paths...] [--staged] [--config/-c PATH] [--exclude/-e PATTERN] [--service NAME] [--json]` | Scans code for hardcoded secrets and for env vars used in code (`os.getenv`, `os.environ.get`, `process.env.X`) but never declared in the schema. `--staged` scans only what's staged for the next commit (what the pre-commit hook runs); `--exclude` (repeatable) adds glob patterns to skip, on top of whatever `secret_scanning.exclude_files` is set in `envshield.yml`. See [Secret scanning and git hooks](#secret-scanning-and-git-hooks). |
|
|
369
370
|
| `envshield install-hook` | Installs both git hooks by hand, without going through `init`/`setup`/`service discover`'s interactive prompt. |
|
|
370
371
|
| `envshield --version` / `-v` | Prints the installed version and exits. |
|
|
371
372
|
|
|
@@ -390,6 +391,63 @@ services:
|
|
|
390
391
|
|
|
391
392
|
---
|
|
392
393
|
|
|
394
|
+
## Machine-readable output (`--json`)
|
|
395
|
+
|
|
396
|
+
`check`, `doctor`, and `scan` all accept `--json`: every Rich table/panel/progress-bar is suppressed, and exactly one JSON object is printed to stdout instead. Exit codes are unchanged (non-zero on any issue), so `--json` is a drop-in for CI gates, dashboards, or an agent loop that needs to branch on the result instead of parsing colored text. If multiple services are configured and `--service` isn't given, `--json` runs every service automatically rather than falling into the interactive "Which service?" picker.
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
envshield check --json
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
```json
|
|
403
|
+
{
|
|
404
|
+
"success": false,
|
|
405
|
+
"results": [
|
|
406
|
+
{
|
|
407
|
+
"file": ".env",
|
|
408
|
+
"service": null,
|
|
409
|
+
"clean": false,
|
|
410
|
+
"missing": [],
|
|
411
|
+
"blank": ["SECRETS_ENCRYPTION_KEY"],
|
|
412
|
+
"invalid": {},
|
|
413
|
+
"extra": []
|
|
414
|
+
}
|
|
415
|
+
]
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
`doctor --json` returns the same per-service shape, with each service's individual health checks instead of a variable diff (`--fix` is rejected together with `--json` — an interactive confirm prompt has no place in a machine-readable mode):
|
|
420
|
+
|
|
421
|
+
```json
|
|
422
|
+
{
|
|
423
|
+
"success": true,
|
|
424
|
+
"results": [
|
|
425
|
+
{
|
|
426
|
+
"service": "hermes",
|
|
427
|
+
"passed": true,
|
|
428
|
+
"checks": [
|
|
429
|
+
{"name": "Configuration Files", "passed": true, "message": "Found and accessible."}
|
|
430
|
+
]
|
|
431
|
+
}
|
|
432
|
+
]
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
`scan --json` returns one flat object (scan doesn't fan out per-service the way check/doctor do — it scans a set of files against whichever schema each one belongs to in a single pass):
|
|
437
|
+
|
|
438
|
+
```json
|
|
439
|
+
{
|
|
440
|
+
"clean": false,
|
|
441
|
+
"secrets": [
|
|
442
|
+
{"file_path": "./config.py", "line_num": 12, "secret_type": "Generic API Key", "line_content": "..."}
|
|
443
|
+
],
|
|
444
|
+
"undeclared_variables": [],
|
|
445
|
+
"skipped_files": []
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
393
451
|
## Typed config code generation
|
|
394
452
|
|
|
395
453
|
Stop writing `os.getenv("DATABASE_URL")` — untyped, unvalidated, and silently `None` on a typo — and generate a real module instead:
|
|
@@ -405,6 +463,7 @@ Source of truth: env.schema.toml. Regenerate with: envshield generate
|
|
|
405
463
|
|
|
406
464
|
Requires: pip install pydantic pydantic-settings
|
|
407
465
|
"""
|
|
466
|
+
|
|
408
467
|
from pydantic import Field, SecretStr
|
|
409
468
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
410
469
|
|
|
@@ -417,11 +476,13 @@ class Settings(BaseSettings):
|
|
|
417
476
|
)
|
|
418
477
|
|
|
419
478
|
database_url: SecretStr = Field(
|
|
420
|
-
...,
|
|
479
|
+
...,
|
|
480
|
+
description="PostgreSQL connection string for the API",
|
|
481
|
+
alias="DATABASE_URL",
|
|
421
482
|
)
|
|
422
483
|
|
|
423
484
|
api_port: int = Field(
|
|
424
|
-
|
|
485
|
+
"5000", description="Port the API listens on", alias="API_PORT", ge=1, le=65535
|
|
425
486
|
)
|
|
426
487
|
|
|
427
488
|
|
|
@@ -431,7 +492,9 @@ settings = Settings()
|
|
|
431
492
|
```python
|
|
432
493
|
from config import settings
|
|
433
494
|
|
|
434
|
-
db = psycopg2.connect(
|
|
495
|
+
db = psycopg2.connect(
|
|
496
|
+
settings.database_url.get_secret_value()
|
|
497
|
+
) # SecretStr — masked in logs/reprs
|
|
435
498
|
port = settings.api_port # a real int, validated 1-65535 on startup — not a string
|
|
436
499
|
```
|
|
437
500
|
|
|
@@ -37,6 +37,7 @@ It works the same way whether you have one repo with one `.env` file, or a monor
|
|
|
37
37
|
- [Command reference](#command-reference)
|
|
38
38
|
- [Core commands](#core-commands)
|
|
39
39
|
- [Monorepo: managing multiple services (monorepo)](#monorepo-managing-multiple-services)
|
|
40
|
+
- [Machine-readable output (`--json`)](#machine-readable-output---json)
|
|
40
41
|
- [Typed config code generation](#typed-config-code-generation)
|
|
41
42
|
- [Validating deployment manifests](#validating-deployment-manifests)
|
|
42
43
|
- [Secret scanning and git hooks](#secret-scanning-and-git-hooks)
|
|
@@ -305,12 +306,12 @@ Everything a single-service project ever needs. `--service` shows up on most of
|
|
|
305
306
|
|---|---|
|
|
306
307
|
| `envshield init [--force/-f]` | Detects your framework and builds `env.schema.toml` from a real config source if it finds one, otherwise a framework-aware template. Also scaffolds `envshield.yml`, updates `.gitignore`, and offers to install git hooks. Auto-registers a root-level `docker-compose.yml` as the project's deployment manifest if it finds one. `--force` re-runs on a project that already has a config (with a confirmation before overwriting). |
|
|
307
308
|
| `envshield import <file> [--output/-o PATH] [--force/-f] [--interactive] [--service NAME]` | Runs the same real-variable analysis `init` does automatically, as its own command — for re-importing after your code gains new variables, pointing at a file `init` wouldn't have found, or adding `--interactive` to confirm each secret/type classification by hand instead of accepting the automatic guess. `--output` changes where the schema is written (defaults to `env.schema.toml`, or the target service's schema path with `--service`). |
|
|
308
|
-
| `envshield check [file] [--service NAME] [--container NAME]` | Validates a local file (or, if omitted, the project's/service's default local file *and* its registered deployment manifest, if any) against the schema. `file` can be a plain `.env`, a Python config module, a docker-compose file, or a Kubernetes manifest. `--container` picks which service/container to check in a manifest that declares more than one (tried against `--service`'s name automatically first). Exits non-zero on any drift — safe to use as a CI gate. |
|
|
309
|
-
| `envshield doctor [--fix] [--service NAME]` | Runs every health check at once (see below) and reports a summary. `--fix` interactively offers to fix whatever it can — re-running `init`, regenerating the template, installing the git hook, or running `setup` to fill in missing/invalid local values. Exits non-zero if anything's still broken afterward. |
|
|
309
|
+
| `envshield check [file] [--service NAME] [--container NAME] [--json]` | Validates a local file (or, if omitted, the project's/service's default local file *and* its registered deployment manifest, if any) against the schema. `file` can be a plain `.env`, a Python config module, a docker-compose file, or a Kubernetes manifest. `--container` picks which service/container to check in a manifest that declares more than one (tried against `--service`'s name automatically first). Exits non-zero on any drift — safe to use as a CI gate. `--json` prints a machine-readable result instead (see below) and never falls into an interactive service picker. |
|
|
310
|
+
| `envshield doctor [--fix] [--service NAME] [--json]` | Runs every health check at once (see below) and reports a summary. `--fix` interactively offers to fix whatever it can — re-running `init`, regenerating the template, installing the git hook, or running `setup` to fill in missing/invalid local values. Exits non-zero if anything's still broken afterward. `--json` is incompatible with `--fix` (an interactive confirm prompt makes no sense in a machine-readable mode). |
|
|
310
311
|
| `envshield setup [output_file] [--service NAME]` | Interactive onboarding wizard: walks through every variable that's missing, blank, or has an existing value the schema no longer allows, prompting with the variable's description, masking secret input, and offering a picker for `enum` fields. Leaves everything already correct untouched. |
|
|
311
312
|
| `envshield schema sync [--service NAME]` | Regenerates `.env.example` from the schema (a dotenv project), or patches a Python-module local file in place to declare any schema variable it's missing (never rewrites it wholesale — only appends/patches the specific lines it owns). `import` already calls this automatically for you when it changes a project's/service's real schema, so you'll rarely need to run it by hand except after a manual schema edit. |
|
|
312
|
-
| `envshield generate [output_file] [--lang/-l python\|typescript] [--force/-f] [--service NAME]` | Compiles the schema into a typed, validated config module. `--lang` is auto-detected from your project (Next.js/Vite/Node.js → TypeScript,
|
|
313
|
-
| `envshield scan [paths...] [--staged] [--config/-c PATH] [--exclude/-e PATTERN]
|
|
313
|
+
| `envshield generate [output_file] [--lang/-l python\|typescript] [--force/-f] [--service NAME]` | Compiles the schema into a typed, validated config module. `--lang` is auto-detected from your project (Next.js/Vite/Node.js → TypeScript; Python/Django/Flask, or nothing detected → Python) if omitted. A detected ecosystem with no codegen target at all (currently: Go) errors and asks for `--lang` explicitly, rather than silently guessing Python. Defaults to writing `config.py`/`config.ts`; `--force` overwrites an existing output file. See [Typed config code generation](#typed-config-code-generation). |
|
|
314
|
+
| `envshield scan [paths...] [--staged] [--config/-c PATH] [--exclude/-e PATTERN] [--service NAME] [--json]` | Scans code for hardcoded secrets and for env vars used in code (`os.getenv`, `os.environ.get`, `process.env.X`) but never declared in the schema. `--staged` scans only what's staged for the next commit (what the pre-commit hook runs); `--exclude` (repeatable) adds glob patterns to skip, on top of whatever `secret_scanning.exclude_files` is set in `envshield.yml`. See [Secret scanning and git hooks](#secret-scanning-and-git-hooks). |
|
|
314
315
|
| `envshield install-hook` | Installs both git hooks by hand, without going through `init`/`setup`/`service discover`'s interactive prompt. |
|
|
315
316
|
| `envshield --version` / `-v` | Prints the installed version and exits. |
|
|
316
317
|
|
|
@@ -335,6 +336,63 @@ services:
|
|
|
335
336
|
|
|
336
337
|
---
|
|
337
338
|
|
|
339
|
+
## Machine-readable output (`--json`)
|
|
340
|
+
|
|
341
|
+
`check`, `doctor`, and `scan` all accept `--json`: every Rich table/panel/progress-bar is suppressed, and exactly one JSON object is printed to stdout instead. Exit codes are unchanged (non-zero on any issue), so `--json` is a drop-in for CI gates, dashboards, or an agent loop that needs to branch on the result instead of parsing colored text. If multiple services are configured and `--service` isn't given, `--json` runs every service automatically rather than falling into the interactive "Which service?" picker.
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
envshield check --json
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
```json
|
|
348
|
+
{
|
|
349
|
+
"success": false,
|
|
350
|
+
"results": [
|
|
351
|
+
{
|
|
352
|
+
"file": ".env",
|
|
353
|
+
"service": null,
|
|
354
|
+
"clean": false,
|
|
355
|
+
"missing": [],
|
|
356
|
+
"blank": ["SECRETS_ENCRYPTION_KEY"],
|
|
357
|
+
"invalid": {},
|
|
358
|
+
"extra": []
|
|
359
|
+
}
|
|
360
|
+
]
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
`doctor --json` returns the same per-service shape, with each service's individual health checks instead of a variable diff (`--fix` is rejected together with `--json` — an interactive confirm prompt has no place in a machine-readable mode):
|
|
365
|
+
|
|
366
|
+
```json
|
|
367
|
+
{
|
|
368
|
+
"success": true,
|
|
369
|
+
"results": [
|
|
370
|
+
{
|
|
371
|
+
"service": "hermes",
|
|
372
|
+
"passed": true,
|
|
373
|
+
"checks": [
|
|
374
|
+
{"name": "Configuration Files", "passed": true, "message": "Found and accessible."}
|
|
375
|
+
]
|
|
376
|
+
}
|
|
377
|
+
]
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
`scan --json` returns one flat object (scan doesn't fan out per-service the way check/doctor do — it scans a set of files against whichever schema each one belongs to in a single pass):
|
|
382
|
+
|
|
383
|
+
```json
|
|
384
|
+
{
|
|
385
|
+
"clean": false,
|
|
386
|
+
"secrets": [
|
|
387
|
+
{"file_path": "./config.py", "line_num": 12, "secret_type": "Generic API Key", "line_content": "..."}
|
|
388
|
+
],
|
|
389
|
+
"undeclared_variables": [],
|
|
390
|
+
"skipped_files": []
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
338
396
|
## Typed config code generation
|
|
339
397
|
|
|
340
398
|
Stop writing `os.getenv("DATABASE_URL")` — untyped, unvalidated, and silently `None` on a typo — and generate a real module instead:
|
|
@@ -350,6 +408,7 @@ Source of truth: env.schema.toml. Regenerate with: envshield generate
|
|
|
350
408
|
|
|
351
409
|
Requires: pip install pydantic pydantic-settings
|
|
352
410
|
"""
|
|
411
|
+
|
|
353
412
|
from pydantic import Field, SecretStr
|
|
354
413
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
355
414
|
|
|
@@ -362,11 +421,13 @@ class Settings(BaseSettings):
|
|
|
362
421
|
)
|
|
363
422
|
|
|
364
423
|
database_url: SecretStr = Field(
|
|
365
|
-
...,
|
|
424
|
+
...,
|
|
425
|
+
description="PostgreSQL connection string for the API",
|
|
426
|
+
alias="DATABASE_URL",
|
|
366
427
|
)
|
|
367
428
|
|
|
368
429
|
api_port: int = Field(
|
|
369
|
-
|
|
430
|
+
"5000", description="Port the API listens on", alias="API_PORT", ge=1, le=65535
|
|
370
431
|
)
|
|
371
432
|
|
|
372
433
|
|
|
@@ -376,7 +437,9 @@ settings = Settings()
|
|
|
376
437
|
```python
|
|
377
438
|
from config import settings
|
|
378
439
|
|
|
379
|
-
db = psycopg2.connect(
|
|
440
|
+
db = psycopg2.connect(
|
|
441
|
+
settings.database_url.get_secret_value()
|
|
442
|
+
) # SecretStr — masked in logs/reprs
|
|
380
443
|
port = settings.api_port # a real int, validated 1-65535 on startup — not a string
|
|
381
444
|
```
|
|
382
445
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "4.4.0"
|