igittigitt 2.2.2__tar.gz → 2.2.4__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.
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.claude-plugin/plugin.json +1 -1
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.env.example +29 -99
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.gitignore +8 -4
- igittigitt-2.2.4/CHANGELOG.md +242 -0
- igittigitt-2.2.4/CONFIG.md +131 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/DEVELOPMENT.md +1 -34
- {igittigitt-2.2.2 → igittigitt-2.2.4}/Makefile +38 -4
- {igittigitt-2.2.2 → igittigitt-2.2.4}/PKG-INFO +24 -19
- {igittigitt-2.2.2 → igittigitt-2.2.4}/README.md +3 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/docs/systemdesign/module_reference.md +5 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/pyproject.toml +24 -19
- {igittigitt-2.2.2 → igittigitt-2.2.4}/skills/python-gitignore/SKILL.md +11 -2
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/__init__conf__.py +1 -1
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/commands/_common.py +6 -2
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/commands/config.py +180 -44
- igittigitt-2.2.4/src/igittigitt/adapters/cli/config_load.py +171 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/context.py +22 -1
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/exit_codes.py +3 -1
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/main.py +6 -4
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/root.py +15 -29
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/config/defaultconfig.d/40-layered-config.toml +12 -3
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/config/defaultconfig.d/90-logging.toml +18 -9
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/config/deploy.py +22 -9
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/config/overrides.py +56 -11
- igittigitt-2.2.4/src/igittigitt/adapters/logging/setup.py +286 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/memory/config.py +3 -2
- igittigitt-2.2.4/src/igittigitt/adapters/memory/logging.py +61 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/application/ports.py +11 -5
- igittigitt-2.2.4/tests/conftest.py +158 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/test_adapters_integration.py +1 -1
- igittigitt-2.2.4/tests/test_cli_config_errors.py +518 -0
- igittigitt-2.2.4/tests/test_cli_main_exit.py +81 -0
- igittigitt-2.2.4/tests/test_config_overrides.py +76 -0
- igittigitt-2.2.4/tests/test_declared_dependencies.py +93 -0
- igittigitt-2.2.4/tests/test_deploy_mode_safety.py +189 -0
- igittigitt-2.2.4/tests/test_logging_dotenv_isolation.py +345 -0
- igittigitt-2.2.4/tests/test_memory_logging.py +68 -0
- igittigitt-2.2.4/tests/test_permission_defaults.py +588 -0
- igittigitt-2.2.2/CHANGELOG.md +0 -119
- igittigitt-2.2.2/CONFIG.md +0 -68
- igittigitt-2.2.2/src/igittigitt/adapters/config/permissions.py +0 -186
- igittigitt-2.2.2/src/igittigitt/adapters/logging/setup.py +0 -134
- igittigitt-2.2.2/src/igittigitt/adapters/memory/logging.py +0 -18
- igittigitt-2.2.2/tests/conftest.py +0 -75
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.claude-plugin/marketplace.json +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.devcontainer/devcontainer.json +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.devcontainer/settings.json +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.github/actions/extract-metadata/action.yml +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.github/dependabot.yml +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.github/workflows/codeql.yml +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.github/workflows/default_cicd_public.yml +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.github/workflows/default_release_public.yml +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.qlty/qlty.toml +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/.snyk +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/CONTRIBUTING.md +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/INSTALL.md +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/LICENSE +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/SECURITY.md +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/ai-disclosure.md +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/ai-stance.md +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/codecov.yml +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/notebooks/Quickstart.ipynb +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/__init__.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/__main__.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/__init__.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/__init__.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/commands/__init__.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/commands/check.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/commands/filter.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/commands/info.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/commands/logging.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/constants.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/py.typed +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/cli/typed_click.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/config/__init__.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/config/defaultconfig.d/50-performance.toml +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/config/defaultconfig.toml +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/config/display.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/config/loader.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/config/performance.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/config/py.typed +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/logging/__init__.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/logging/py.typed +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/memory/__init__.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/adapters/py.typed +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/application/__init__.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/application/py.typed +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/composition/__init__.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/composition/py.typed +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/conf_igittigitt.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/domain/__init__.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/domain/enums.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/domain/py.typed +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/entry.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/igittigitt.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/src/igittigitt/py.typed +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/default_pattern/.config/git/ignore +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/default_pattern/git/ignore +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/.test_gitignore +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/.test_gitignore_empty +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/.test_venv/some_file.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/not_excluded/.test_gitignore +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/not_excluded/excluded/excluded.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/not_excluded/excluded_not/sub_excluded.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/not_excluded/excluded_not.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/not_excluded/not_excluded2/sub_excluded.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/not_excluded/not_excluded2.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/not_excluded.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/test__pycache__/.test_gitignore +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/test__pycache__/excluded/excluded/excluded.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/test__pycache__/some_file.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example/test__pycache__/test +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example_negation/foo/bar/file.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/example_negation/foo/other/file.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/target_expected/.test_gitignore +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/target_expected/not_excluded/.test_gitignore +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/target_expected/not_excluded/excluded_not.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/target_expected/not_excluded/not_excluded2.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/target_expected/not_excluded.txt +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/test_cli.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/test_git_compat.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/test_include.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/test_memory.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/test_metadata.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/test_perf_config.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/test_pytest.py +0 -0
- {igittigitt-2.2.2 → igittigitt-2.2.4}/tests/test_shell_pipe.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "igittigitt",
|
|
3
3
|
"description": "Parse .gitignore files and filter/whitelist large numbers of paths in .gitignore style from Python or the shell. Ships the python-gitignore skill: install, configure (config-deploy + config.d overrides), library API (IgnoreParser/IncludeParser, shutil.copytree filter), the CLI, and bash piping.",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.2.6",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "bitranox"
|
|
7
7
|
},
|
|
@@ -11,83 +11,6 @@ PYPI_API_TOKEN=
|
|
|
11
11
|
GITHUB_TOKEN=
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
# ==============================================================================
|
|
15
|
-
# Email Configuration (SMTP)
|
|
16
|
-
# ==============================================================================
|
|
17
|
-
#
|
|
18
|
-
# Naming Convention
|
|
19
|
-
# -----------------
|
|
20
|
-
# Format: <SECTION>__<KEY>=value (double underscore separates section from key)
|
|
21
|
-
# Lists use comma-separated values, booleans use true/false, floats use decimal notation.
|
|
22
|
-
#
|
|
23
|
-
# Full environment variable (outside .env):
|
|
24
|
-
# IGITTIGITT___EMAIL__<KEY>=value
|
|
25
|
-
# (triple underscore separates the application slug from the section)
|
|
26
|
-
|
|
27
|
-
# Purpose: SMTP servers for email delivery, tried in order (host:port, comma-separated)
|
|
28
|
-
# Type: comma-separated strings in "host[:port]" format
|
|
29
|
-
# Default: (empty, must be configured to send email)
|
|
30
|
-
# Environment Variable: IGITTIGITT___EMAIL__SMTP_HOSTS=smtp.example.com:587
|
|
31
|
-
# .env: EMAIL__SMTP_HOSTS=smtp.example.com:587
|
|
32
|
-
# EMAIL__SMTP_HOSTS=smtp.example.com:587
|
|
33
|
-
|
|
34
|
-
# Purpose: Default sender address for outgoing emails
|
|
35
|
-
# Type: string (valid email address)
|
|
36
|
-
# Default: (empty, must be configured or passed via --from)
|
|
37
|
-
# Environment Variable: IGITTIGITT___EMAIL__FROM_ADDRESS=noreply@example.com
|
|
38
|
-
# .env: EMAIL__FROM_ADDRESS=noreply@example.com
|
|
39
|
-
# EMAIL__FROM_ADDRESS=noreply@example.com
|
|
40
|
-
|
|
41
|
-
# Purpose: Default recipient addresses (comma-separated)
|
|
42
|
-
# Type: comma-separated strings (valid email addresses)
|
|
43
|
-
# Default: (empty, must be configured or passed via --to)
|
|
44
|
-
# Environment Variable: IGITTIGITT___EMAIL__RECIPIENTS=admin@example.com,ops@example.com
|
|
45
|
-
# .env: EMAIL__RECIPIENTS=admin@example.com,ops@example.com
|
|
46
|
-
# EMAIL__RECIPIENTS=admin@example.com,ops@example.com
|
|
47
|
-
|
|
48
|
-
# Purpose: SMTP authentication username
|
|
49
|
-
# Type: string
|
|
50
|
-
# Default: (empty, no authentication)
|
|
51
|
-
# Environment Variable: IGITTIGITT___EMAIL__SMTP_USERNAME=noreply@example.com
|
|
52
|
-
# .env: EMAIL__SMTP_USERNAME=noreply@example.com
|
|
53
|
-
# EMAIL__SMTP_USERNAME=noreply@example.com
|
|
54
|
-
|
|
55
|
-
# Purpose: SMTP authentication password
|
|
56
|
-
# Type: string
|
|
57
|
-
# Default: (empty, no authentication)
|
|
58
|
-
# Environment Variable: IGITTIGITT___EMAIL__SMTP_PASSWORD=your-app-password
|
|
59
|
-
# .env: EMAIL__SMTP_PASSWORD=your-app-password
|
|
60
|
-
# EMAIL__SMTP_PASSWORD=your-app-password
|
|
61
|
-
|
|
62
|
-
# Purpose: Enable STARTTLS negotiation before authentication
|
|
63
|
-
# Type: boolean (true/false)
|
|
64
|
-
# Default: true
|
|
65
|
-
# Environment Variable: IGITTIGITT___EMAIL__USE_STARTTLS=true
|
|
66
|
-
# .env: EMAIL__USE_STARTTLS=true
|
|
67
|
-
# EMAIL__USE_STARTTLS=true
|
|
68
|
-
|
|
69
|
-
# Purpose: Socket timeout in seconds for SMTP operations
|
|
70
|
-
# Type: float
|
|
71
|
-
# Default: 30.0
|
|
72
|
-
# Environment Variable: IGITTIGITT___EMAIL__TIMEOUT=30.0
|
|
73
|
-
# .env: EMAIL__TIMEOUT=30.0
|
|
74
|
-
# EMAIL__TIMEOUT=30.0
|
|
75
|
-
|
|
76
|
-
# Purpose: Raise FileNotFoundError when attachment files are missing
|
|
77
|
-
# Type: boolean (true/false)
|
|
78
|
-
# Default: true
|
|
79
|
-
# Environment Variable: IGITTIGITT___EMAIL__RAISE_ON_MISSING_ATTACHMENTS=true
|
|
80
|
-
# .env: EMAIL__RAISE_ON_MISSING_ATTACHMENTS=true
|
|
81
|
-
# EMAIL__RAISE_ON_MISSING_ATTACHMENTS=true
|
|
82
|
-
|
|
83
|
-
# Purpose: Raise ValueError when recipient addresses are invalid
|
|
84
|
-
# Type: boolean (true/false)
|
|
85
|
-
# Default: true
|
|
86
|
-
# Environment Variable: IGITTIGITT___EMAIL__RAISE_ON_INVALID_RECIPIENT=true
|
|
87
|
-
# .env: EMAIL__RAISE_ON_INVALID_RECIPIENT=true
|
|
88
|
-
# EMAIL__RAISE_ON_INVALID_RECIPIENT=true
|
|
89
|
-
|
|
90
|
-
|
|
91
14
|
# ==============================================================================
|
|
92
15
|
# Logging Configuration (lib_log_rich)
|
|
93
16
|
# ==============================================================================
|
|
@@ -95,7 +18,14 @@ GITHUB_TOKEN=
|
|
|
95
18
|
# Naming Convention
|
|
96
19
|
# -----------------
|
|
97
20
|
# Format: <SECTION>__<KEY>=value (double underscore separates section from key)
|
|
98
|
-
# Strings need no quotes.
|
|
21
|
+
# Strings need no quotes. An unquoted value is converted (in .env and in the environment
|
|
22
|
+
# alike): true/false become booleans, null/none become None, a JSON array or object is parsed,
|
|
23
|
+
# and a number becomes a number only when it converts back to the same text (007 and 1.50 stay
|
|
24
|
+
# text); quote a .env value to keep it as text. A list is a JSON array and a table a JSON
|
|
25
|
+
# object, written UNQUOTED in .env (LIB_LOG_RICH__CONSOLE_STYLES={"ERROR":"bold red"}), or one
|
|
26
|
+
# key per entry (LIB_LOG_RICH__SCRUB_PATTERNS__API_KEY=.+). A comma-separated value is NOT a
|
|
27
|
+
# list or a table: it arrives as ONE string and is refused. In the environment, quote a JSON
|
|
28
|
+
# value for the shell: IGITTIGITT___LIB_LOG_RICH__RATE_LIMIT='[100,60]'.
|
|
99
29
|
#
|
|
100
30
|
# Full environment variable (outside .env):
|
|
101
31
|
# IGITTIGITT___LIB_LOG_RICH__<KEY>=value
|
|
@@ -157,12 +87,12 @@ GITHUB_TOKEN=
|
|
|
157
87
|
# .env: LIB_LOG_RICH__CONSOLE_THEME=dark
|
|
158
88
|
# LIB_LOG_RICH__CONSOLE_THEME=dark
|
|
159
89
|
|
|
160
|
-
# Purpose: Override Rich styles per severity level
|
|
161
|
-
# Type:
|
|
90
|
+
# Purpose: Override Rich styles per severity level
|
|
91
|
+
# Type: table of LEVEL = style (a JSON object keeps the level names upper case)
|
|
162
92
|
# Default: (empty, uses theme defaults)
|
|
163
|
-
# Environment Variable: IGITTIGITT___LIB_LOG_RICH__CONSOLE_STYLES=DEBUG
|
|
164
|
-
# .env: LIB_LOG_RICH__CONSOLE_STYLES=DEBUG
|
|
165
|
-
# LIB_LOG_RICH__CONSOLE_STYLES=DEBUG
|
|
93
|
+
# Environment Variable: IGITTIGITT___LIB_LOG_RICH__CONSOLE_STYLES='{"DEBUG":"dim","ERROR":"bold red"}'
|
|
94
|
+
# .env: LIB_LOG_RICH__CONSOLE_STYLES={"DEBUG":"dim","ERROR":"bold red"}
|
|
95
|
+
# LIB_LOG_RICH__CONSOLE_STYLES={"DEBUG":"dim","ERROR":"bold red"}
|
|
166
96
|
|
|
167
97
|
# Purpose: Force ANSI color output even when stderr is not a TTY
|
|
168
98
|
# Type: boolean (true/false)
|
|
@@ -210,12 +140,12 @@ GITHUB_TOKEN=
|
|
|
210
140
|
# .env: LIB_LOG_RICH__ENABLE_GRAYLOG=true
|
|
211
141
|
# LIB_LOG_RICH__ENABLE_GRAYLOG=true
|
|
212
142
|
|
|
213
|
-
# Purpose: Graylog server address
|
|
214
|
-
# Type:
|
|
143
|
+
# Purpose: Graylog server address
|
|
144
|
+
# Type: JSON array [host, port]; a "host:port" string is refused
|
|
215
145
|
# Default: (none, must be configured to enable Graylog)
|
|
216
|
-
# Environment Variable: IGITTIGITT___LIB_LOG_RICH__GRAYLOG_ENDPOINT=graylog.example.com
|
|
217
|
-
# .env: LIB_LOG_RICH__GRAYLOG_ENDPOINT=graylog.example.com
|
|
218
|
-
# LIB_LOG_RICH__GRAYLOG_ENDPOINT=graylog.example.com
|
|
146
|
+
# Environment Variable: IGITTIGITT___LIB_LOG_RICH__GRAYLOG_ENDPOINT='["graylog.example.com",12201]'
|
|
147
|
+
# .env: LIB_LOG_RICH__GRAYLOG_ENDPOINT=["graylog.example.com",12201]
|
|
148
|
+
# LIB_LOG_RICH__GRAYLOG_ENDPOINT=["graylog.example.com",12201]
|
|
219
149
|
|
|
220
150
|
# Purpose: Transport protocol for Graylog (tcp, udp); TLS only supported with tcp
|
|
221
151
|
# Type: string: "tcp", "udp"
|
|
@@ -309,19 +239,19 @@ GITHUB_TOKEN=
|
|
|
309
239
|
|
|
310
240
|
# --- Security & Payload ---
|
|
311
241
|
|
|
312
|
-
# Purpose: Regex patterns to redact sensitive data (
|
|
313
|
-
# Type:
|
|
314
|
-
# Default: password
|
|
315
|
-
# Environment Variable:
|
|
316
|
-
# .env:
|
|
317
|
-
#
|
|
242
|
+
# Purpose: Regex patterns to redact sensitive data (field = regex)
|
|
243
|
+
# Type: table of field = regex; merged with the defaults
|
|
244
|
+
# Default: password = .+, secret = .+, token = .+
|
|
245
|
+
# Environment Variable, one key per field: IGITTIGITT___LIB_LOG_RICH__SCRUB_PATTERNS__API_KEY=.+
|
|
246
|
+
# .env, one key per field: LIB_LOG_RICH__SCRUB_PATTERNS__API_KEY=.+
|
|
247
|
+
# LIB_LOG_RICH__SCRUB_PATTERNS__API_KEY=.+
|
|
318
248
|
|
|
319
|
-
# Purpose: Throttle log emissions (
|
|
320
|
-
# Type:
|
|
249
|
+
# Purpose: Throttle log emissions (max_events per window_seconds)
|
|
250
|
+
# Type: JSON array [max_events, window_seconds]; a "100:60" string is refused
|
|
321
251
|
# Default: (none, no rate limiting)
|
|
322
|
-
# Environment Variable: IGITTIGITT___LIB_LOG_RICH__RATE_LIMIT=100
|
|
323
|
-
# .env: LIB_LOG_RICH__RATE_LIMIT=100
|
|
324
|
-
# LIB_LOG_RICH__RATE_LIMIT=100
|
|
252
|
+
# Environment Variable: IGITTIGITT___LIB_LOG_RICH__RATE_LIMIT='[100,60]'
|
|
253
|
+
# .env: LIB_LOG_RICH__RATE_LIMIT=[100,60]
|
|
254
|
+
# LIB_LOG_RICH__RATE_LIMIT=[100,60]
|
|
325
255
|
|
|
326
256
|
# --- Payload Limits ---
|
|
327
257
|
|
|
@@ -47,7 +47,8 @@ tmp_*
|
|
|
47
47
|
__pycache__/
|
|
48
48
|
src/igittigitt/__pycache__/
|
|
49
49
|
*.py[cod]
|
|
50
|
-
|
|
50
|
+
# other compiled artifacts
|
|
51
|
+
*$py.class
|
|
51
52
|
|
|
52
53
|
# pytest, coverage, MyPy, Hypothesis
|
|
53
54
|
.pytest_cache/
|
|
@@ -73,12 +74,15 @@ coverage.xml
|
|
|
73
74
|
# source/package builds
|
|
74
75
|
build/
|
|
75
76
|
dist/
|
|
76
|
-
|
|
77
|
+
# metadata for pip installs
|
|
78
|
+
*.egg-info/
|
|
77
79
|
*.egg
|
|
78
80
|
*.whl
|
|
79
81
|
pip-wheel-metadata/
|
|
80
|
-
|
|
81
|
-
*.
|
|
82
|
+
# PyInstaller metadata
|
|
83
|
+
*.manifest
|
|
84
|
+
# PyInstaller spec files
|
|
85
|
+
*.spec
|
|
82
86
|
|
|
83
87
|
# Eggs, parts, wheels, sdist, etc.
|
|
84
88
|
develop-eggs/
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file following
|
|
4
|
+
the [Keep a Changelog](https://keepachangelog.com/) format.
|
|
5
|
+
|
|
6
|
+
This project adheres to semantic versioning: MAJOR for incompatible API changes,
|
|
7
|
+
MINOR for backwards-compatible functionality, PATCH for backwards-compatible fixes.
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
## [2.2.4] 2026-10-08 10:19:56
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **`build_testing()` can run a command.** Every CLI command except `check` and `filter` binds job
|
|
16
|
+
context onto the process-global lib_log_rich runtime, but the testing composition's
|
|
17
|
+
`init_logging` was a no-op, so `info`, `config` and the deploy commands raised
|
|
18
|
+
`RuntimeError('lib_log_rich.init() must be called before using the logging API')` under
|
|
19
|
+
`build_testing()`. The in-memory initializer now starts a quiet runtime: journald, event log,
|
|
20
|
+
Graylog and the queue off, console at ERROR, and no `.env` loading.
|
|
21
|
+
- **`main()` keeps the exit code of a command that exits through click's context.** rich_click's
|
|
22
|
+
`main()` returns the code of a `ctx.exit(N)` under `standalone_mode=False`; `main()` discarded
|
|
23
|
+
that return value and reported 0, and its `except click.exceptions.Exit` branch could never
|
|
24
|
+
fire. It now returns the code (exit code change: such a command exits N instead of 0).
|
|
25
|
+
- **Conflicting `--set` overrides are a usage error.** `--set a.b=1 --set a.b.c=2` escaped as a
|
|
26
|
+
`TypeError` from the override nesting, and the other order, `--set a.b.c=2 --set a.b=1`,
|
|
27
|
+
silently dropped the earlier override. All `--set` values are now checked together and a key
|
|
28
|
+
that one gives a value and another puts a key under is refused, naming both (exit code change:
|
|
29
|
+
exit 2 for every command, where the second order used to exit 0). The same key given twice still
|
|
30
|
+
takes the last value; `a.b` and `a.bc` stay siblings.
|
|
31
|
+
- **A broken configuration file no longer disables every command.** The root group loaded the
|
|
32
|
+
configuration before any subcommand option was parsed and let a load error escape, so a
|
|
33
|
+
malformed `config.toml`, a `.env` that is not UTF-8 or an unreadable file made every command,
|
|
34
|
+
`--help` and `config-deploy` (the command that replaces the file) exit 1. The root now records
|
|
35
|
+
the failure (`adapters/cli/config_load.py`): `config`, `check` and `filter`, which read the
|
|
36
|
+
configuration, refuse with exit 78 and one line naming the file, after the loader's traceback
|
|
37
|
+
with `--traceback`; `info`, `config-deploy`, `config-generate-examples` and help still run
|
|
38
|
+
(exit code change: 78 or 0 where every command exited 1). What the command line gets wrong is
|
|
39
|
+
checked before loading, so a broken file cannot hide it: a malformed or conflicting `--set` or
|
|
40
|
+
an invalid `--profile` name is a usage error (exit 2) for every command, where an invalid
|
|
41
|
+
root `--profile` used to exit 22 and an invalid `config-deploy --profile` failed inside the
|
|
42
|
+
deploy with exit 1. Any other exception from the loader is a bug and propagates as one.
|
|
43
|
+
`config --profile X` reloads with the root's `--env-file` instead of searching for another
|
|
44
|
+
`.env`.
|
|
45
|
+
- **Logging takes only `LOG_*` lines from a `.env`.** `init_logging` called lib_log_rich's
|
|
46
|
+
`enable_dotenv()`, which copied every line of the nearest `.env` into the process environment,
|
|
47
|
+
so a later configuration load (`config --profile`, the deploy's permission read) took an
|
|
48
|
+
app-prefixed `.env` line for the environment layer: a prefixed `default_permissions` line in the
|
|
49
|
+
working directory's `.env` refused `config-deploy`, and a prefixed `[performance]` value showed
|
|
50
|
+
up in `config --profile`. Logging now copies only `LOG_*` lines, never over a variable that is
|
|
51
|
+
already set, from `--env-file` when given, otherwise from the nearest `.env` up to the project
|
|
52
|
+
root, without `chdir` and passing over unreadable directories; a `.env` that is not UTF-8 no
|
|
53
|
+
longer stops logging.
|
|
54
|
+
- **An invalid `[lib_log_rich]` value is a configuration failure, not a crash of every command.**
|
|
55
|
+
A value lib_log_rich refuses (a wrong type, or its own range checks such as `queue_maxsize = 0`)
|
|
56
|
+
stopped every command with exit 22 and pydantic's multi-line report. It is now recorded like a load
|
|
57
|
+
failure: logging starts with its defaults, `config`, `check` and `filter` exit 78 with one
|
|
58
|
+
`Error:` line per problem naming the key, never the value, and `info`, `config-deploy` and help
|
|
59
|
+
run (exit code change). The `InitLogging` port takes `dotenv_path`, and the root types the
|
|
60
|
+
services factory instead of ignoring the type.
|
|
61
|
+
- **A refused `LOG_*` variable no longer disables every command (exit code change).** A value
|
|
62
|
+
lib_log_rich refuses in a `LOG_*` variable, set in the environment or in the `.env` logging reads
|
|
63
|
+
(`LOG_CONSOLE_LEVEL=bogus`), made every command but a bare `--help` exit 22 with lib_log_rich's
|
|
64
|
+
`ValueError`. Logging now falls back to its defaults with every `LOG_*` variable hidden for that
|
|
65
|
+
start (and put back afterwards), so only `config`, `check` and `filter` exit 78, and `info`,
|
|
66
|
+
`config-deploy` and the help of every command run with exit 0. The 78 carries lib_log_rich's own
|
|
67
|
+
message, `Error: lib_log_rich: Unknown log level: 'bogus'`, which may name neither the variable
|
|
68
|
+
nor where it was set. A refused `[lib_log_rich]` value still leaves every valid `LOG_*` variable
|
|
69
|
+
in force for the fallback: only a refused variable hides them.
|
|
70
|
+
- **`config-deploy` leaves every permission decision to lib_layered_config.** The command read
|
|
71
|
+
`[lib_layered_config.default_permissions]` from its own merged configuration, so a `.env` found
|
|
72
|
+
upward from the working directory decided or blocked a system deploy, and of the whole section
|
|
73
|
+
only `enabled` was ever used: the configured per-layer modes never reached a deploy, and a
|
|
74
|
+
section that was not a table crashed with `AttributeError`. It now passes its options and any
|
|
75
|
+
`--set` of that section to `deploy_config` unchanged; the library reads the section from the
|
|
76
|
+
bundled defaults, the files the deploy does not overwrite and the environment, never from
|
|
77
|
+
`.env`, and applies each target's own layer modes. A refused setting exits 78 with one `Error:`
|
|
78
|
+
line per problem naming the key and its source, plus a hint in the CLI's spelling when both mode
|
|
79
|
+
options would get past it (exit code change: 78 where an invalid section used to exit 1 or
|
|
80
|
+
deploy). "Deployed configuration" is logged after the deploy, and the report says
|
|
81
|
+
"(permissions not set)" only for an explicit `--no-permissions`.
|
|
82
|
+
- **The documented `.env` and environment syntax for lists and tables works.** `.env.example`,
|
|
83
|
+
`90-logging.toml` and `CONFIG.md` showed comma-separated `LEVEL=style` and `field=regex` pairs
|
|
84
|
+
for `console_styles` and `scrub_patterns` and `host:port` / `100:60` strings for
|
|
85
|
+
`graylog_endpoint` and `rate_limit`; each arrives as ONE string and is refused. They now show
|
|
86
|
+
a JSON array or object (unquoted in `.env`, shell-quoted in the environment) or one key per entry
|
|
87
|
+
(`LIB_LOG_RICH__SCRUB_PATTERNS__API_KEY=.+`), and say how an unquoted value is converted.
|
|
88
|
+
- **Tests no longer depend on test order, colour or terminal width.** An autouse fixture shuts the
|
|
89
|
+
logging runtime down and restores the root logger's handlers, level and propagate flag after
|
|
90
|
+
every test (production `init_logging` attaches a stdlib handler and raises the root level, which
|
|
91
|
+
`runtime.shutdown()` does not undo). A second one pins rich-click's colour and width globals,
|
|
92
|
+
which it reads from the terminal and from GITHUB_ACTIONS once at import, so a usage-error box
|
|
93
|
+
no longer wraps a message the test looks for on a narrow terminal.
|
|
94
|
+
|
|
95
|
+
### Changed
|
|
96
|
+
|
|
97
|
+
- **Requires lib_layered_config 7.0.1**, whose `deploy_config` reads and validates the permission
|
|
98
|
+
section itself. An unquoted `.env` value now converts like the environment layer, so
|
|
99
|
+
`ENABLED=false` arrives as the boolean `false`.
|
|
100
|
+
- **`--no-permissions` together with `--dir-mode` or `--file-mode` is a usage error** (exit 2):
|
|
101
|
+
a mode cannot be applied while permission setting is off.
|
|
102
|
+
- **`python-dotenv` is a declared dependency** (logging reads the `LOG_*` lines of a `.env` with
|
|
103
|
+
it), and lib_log_rich is required at 6.3.9, the version the logging refusal is tested against.
|
|
104
|
+
- **`click` is a declared dependency.** The package imports it directly (`adapters/cli/main.py`)
|
|
105
|
+
but only had it through rich-click. A new test fails when a module imported at run time is
|
|
106
|
+
missing from `[project].dependencies`.
|
|
107
|
+
|
|
108
|
+
### Removed
|
|
109
|
+
|
|
110
|
+
- `adapters.config.permissions` (`PermissionDefaults`, `parse_mode`, `get_permission_defaults`,
|
|
111
|
+
`get_modes_for_target`): lib_layered_config reads and validates the section now. `parse_mode`
|
|
112
|
+
silently fell back to the default on a malformed value, and `get_modes_for_target` had no
|
|
113
|
+
production caller, which is why the configured modes never took effect.
|
|
114
|
+
|
|
115
|
+
### Security
|
|
116
|
+
|
|
117
|
+
- **`config-deploy` refuses unsafe and malformed modes.** `--dir-mode -1` passed the unbounded
|
|
118
|
+
octal parser and reached the deploy as mode -1, and `--dir-mode 777` or `--file-mode 666` were
|
|
119
|
+
applied as given. `--dir-mode`/`--file-mode` and the configured modes are now parsed by
|
|
120
|
+
lib_layered_config's `DeployMode`: only a plain octal literal inside 0..0o7777 is accepted, and
|
|
121
|
+
setuid/setgid/sticky, group or world write, an execute bit on a file and a mode that takes away
|
|
122
|
+
the owner's access are refused (exit 2 for an option, 78 for a configured value). A bare integer
|
|
123
|
+
in the configuration is refused because it is read as decimal (`400` is `0o620`).
|
|
124
|
+
|
|
125
|
+
## [2.2.3] 2026-07-30 18:08:55
|
|
126
|
+
|
|
127
|
+
### Changed
|
|
128
|
+
|
|
129
|
+
- **The shipped skill's plugin version now tracks the package version.** bmk 3.14.0 raises
|
|
130
|
+
`.claude-plugin/plugin.json` to the package version on bump, push and release, and never lowers
|
|
131
|
+
it. An install re-fetches a skill only when that version changes, so the two numbers drifting
|
|
132
|
+
apart meant a skill edit could ship to nobody. No functional change to the library.
|
|
133
|
+
|
|
134
|
+
## [2.2.2] 2026-07-24 18:15:33
|
|
135
|
+
|
|
136
|
+
### Fixed
|
|
137
|
+
- Latest `ruff` (0.16+) widened default rule coverage; the project's curated
|
|
138
|
+
`[tool.ruff.lint]` blanket ignore list (`RUF002`, `RUF022`, `PLC0415`, `TC001-3`,
|
|
139
|
+
`TC006`) is removed and every violation it was masking is fixed at the root
|
|
140
|
+
instead: keyword-only params on the six flagged Click callbacks/helpers
|
|
141
|
+
(`build_ignore_parser`, `cli_check`, `cli_filter`, `cli_config_deploy`,
|
|
142
|
+
`_execute_deploy`, `create_rule_variations`) rather than adding rule
|
|
143
|
+
suppressions, an ASCII hyphen in place of an ambiguous en-dash in a docstring,
|
|
144
|
+
and top-level (rather than deferred) imports where no circular-import or
|
|
145
|
+
lazy-load reason applied. A doctest in `adapters/config/permissions.py` that
|
|
146
|
+
relied on `DeployTarget` moving out of `TYPE_CHECKING` is made self-contained.
|
|
147
|
+
- Deferred imports in `tests/*.py` are now a declared per-file ignore
|
|
148
|
+
(`PLC0415`) rather than silently unflagged - they are a deliberate test idiom
|
|
149
|
+
(exercising import/cache behaviour), not an oversight.
|
|
150
|
+
|
|
151
|
+
### Changed
|
|
152
|
+
- CI (template-managed, from `default_cicd_public`): `pip-audit` now audits this
|
|
153
|
+
project's own resolved dependency tree instead of the whole `--system`
|
|
154
|
+
environment, so runner-image packages we neither declare nor ship (`setuptools`,
|
|
155
|
+
`pip`, `wheel`) can no longer report findings against us. `actions/cache` moves
|
|
156
|
+
to v6 in the same distribution.
|
|
157
|
+
- Dev dependency floor: `httpx2>=2.7.0` (was `>=2.6.0`).
|
|
158
|
+
- `[tool.ruff.lint.flake8-type-checking].runtime-evaluated-base-classes` now
|
|
159
|
+
lists `pydantic.BaseModel`, so the `TC00x` autofix leaves Pydantic model base
|
|
160
|
+
classes importable at runtime instead of moving them into `TYPE_CHECKING`.
|
|
161
|
+
- `skills/python-gitignore/SKILL.md` example code blocks reformatted to match
|
|
162
|
+
ruff's latest formatting (cosmetic only; no semantic change to the skill).
|
|
163
|
+
`.claude-plugin/plugin.json` bumped to `1.0.1` to ship the change.
|
|
164
|
+
|
|
165
|
+
## [2.2.1] - 2026-07-14
|
|
166
|
+
|
|
167
|
+
A maintenance release: no library or CLI behaviour changes beyond the profile
|
|
168
|
+
validation fix below. The public API is unchanged.
|
|
169
|
+
|
|
170
|
+
### Fixed
|
|
171
|
+
- `validate_profile()` raises `ValueError` for every invalid profile again. Newer
|
|
172
|
+
`lib_layered_config` raises its own `ValidationError`, which escaped the
|
|
173
|
+
documented `ValueError` contract and slipped past callers' `except ValueError`
|
|
174
|
+
guards; the dependency's exception is now normalised back to `ValueError`.
|
|
175
|
+
- Pyright strict passes against click 8.3+. The `argument` decorator's re-exported
|
|
176
|
+
signature carries an unknown `type` parameter, so it joins `option` and
|
|
177
|
+
`version_option` behind a fully-typed wrapper in `adapters/cli/typed_click`
|
|
178
|
+
instead of the rule being disabled.
|
|
179
|
+
|
|
180
|
+
### Documentation
|
|
181
|
+
- `ExitCode`'s docstring no longer claims the application never exits with a
|
|
182
|
+
signal code. `BROKEN_PIPE` (141) is raised directly by `check` and `filter` when
|
|
183
|
+
their output pipe closes early; only 130 and 143 are informational.
|
|
184
|
+
- `INSTALL.md`: the install-from-tag example pointed at `v1.1.0`, a tag that was
|
|
185
|
+
never cut, and the closing line named the installed command twice. Both fixed.
|
|
186
|
+
|
|
187
|
+
### Changed
|
|
188
|
+
- `codecov-cli` is disabled as a dev dependency. It pins `click<8.3.0`, which both
|
|
189
|
+
held click at a version with a known `click.edit()` command-injection flaw and
|
|
190
|
+
silently backtracked `bmk` to 3.1.7. CI uploads coverage via
|
|
191
|
+
`codecov/codecov-action`, so nothing is lost.
|
|
192
|
+
- The bmk-managed `Makefile` regenerates at 3.6.0 (from 2.9.5), and bmk now lives
|
|
193
|
+
in a per-project `.venv-bmk` tool environment rather than a shared one. This is
|
|
194
|
+
the first bmk update the repo has picked up since the `click` pin was removed.
|
|
195
|
+
- The `[tool.pip-audit]` ignore list is now empty. Every entry it carried had
|
|
196
|
+
become inert (fixed upstream, or naming a package absent from the dependency
|
|
197
|
+
tree), and a stale entry silently suppresses any future advisory filed under the
|
|
198
|
+
same id.
|
|
199
|
+
|
|
200
|
+
## [2.2.0] - 2026-06-26
|
|
201
|
+
|
|
202
|
+
Full rebuild of the project scaffolding on the `bitranox_template_py_cli` template
|
|
203
|
+
(src layout, hatchling, ruff + pyright strict, clean-architecture CLI). The public
|
|
204
|
+
library API (`import igittigitt; igittigitt.IgnoreParser`) is unchanged.
|
|
205
|
+
|
|
206
|
+
### Added
|
|
207
|
+
- **`IncludeParser`** - a directory-aware include / whitelist mode with
|
|
208
|
+
`shutil_include` for use as a `shutil.copytree` filter.
|
|
209
|
+
- **Scriptable CLI** built on rich-click: `check` (mirrors `git check-ignore`,
|
|
210
|
+
including `-v`) and `filter` (a streaming Unix filter reading paths from stdin),
|
|
211
|
+
with newline- or NUL-separated (`-z`) I/O and clean SIGPIPE handling. Plus
|
|
212
|
+
`info`, `config`, `config-deploy`, `config-generate-examples` and `logdemo`.
|
|
213
|
+
- **Layered configuration** (`lib_layered_config`) and **structured logging**
|
|
214
|
+
(`lib_log_rich`). All engine/CLI tuning knobs are exposed and documented in the
|
|
215
|
+
`[performance]` config section (`dir_cache_max`, `pattern_cache_max`,
|
|
216
|
+
`stdin_chunk_bytes`, `max_token_bytes`) and overridable via file / `.env` / env
|
|
217
|
+
var / `--set`.
|
|
218
|
+
- Differential test suite comparing igittigitt against real `git check-ignore`,
|
|
219
|
+
plus memory-boundedness, include, config-knob and real-pipe/SIGPIPE tests.
|
|
220
|
+
|
|
221
|
+
### Changed
|
|
222
|
+
- **Matching is now 100% git-compatible.** The engine performs a single ordered
|
|
223
|
+
pass (last-matching-pattern-wins) with correct negations, parent-directory
|
|
224
|
+
exclusion blocking re-inclusion, and per-directory `.gitignore` precedence. This
|
|
225
|
+
resolves the long-standing negation and nested-precedence limitations.
|
|
226
|
+
- Matching is memory-bounded: per-path cost is `O(depth x rules)` and memory scales
|
|
227
|
+
with the number of rules, not the number of files.
|
|
228
|
+
- Matching is ~5x faster: globs are translated and compiled to `re.Pattern` once
|
|
229
|
+
(cached), and the ancestor-directory decision is memoized in a bounded LRU - both
|
|
230
|
+
keep memory bounded and preserve 100% git compatibility.
|
|
231
|
+
- Dropped the `attrs` runtime dependency in favour of `dataclasses` and `pydantic`.
|
|
232
|
+
- Python baseline raised to **3.10+** (was 3.8).
|
|
233
|
+
- CLI migrated from `click` to `rich-click` with `lib_cli_exit_tools`.
|
|
234
|
+
- Documentation moved from reStructuredText to Markdown.
|
|
235
|
+
|
|
236
|
+
### Removed
|
|
237
|
+
- The old two-phase (sort-and-deduplicate) rule evaluation that broke negations.
|
|
238
|
+
|
|
239
|
+
## [2.1.5] - 2024-10-16
|
|
240
|
+
- Final release on the previous (PizzaCutter / setuptools-scm) scaffolding.
|
|
241
|
+
- Earlier history: a spec-compliant gitignore parser whose negation and nested
|
|
242
|
+
precedence handling was known to be incomplete (see this release's README).
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
igittigitt is configured with [lib_layered_config](https://github.com/bitranox/lib_layered_config).
|
|
4
|
+
Settings are merged from several layers; later layers win:
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
bundled defaults -> app -> host -> user -> .env file -> environment variables -> --set
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
- **Inspect** the merged result (with provenance for each value): `igittigitt config`
|
|
11
|
+
- **Deploy** editable copies into the standard config directories: `igittigitt config-deploy`
|
|
12
|
+
- **Examples** to copy and edit: `igittigitt config-generate-examples`
|
|
13
|
+
|
|
14
|
+
## Where config files live
|
|
15
|
+
|
|
16
|
+
| Layer | Linux | macOS |
|
|
17
|
+
|-------|--------------------------------------------------|--------------------------------------------------------------|
|
|
18
|
+
| app | `/etc/xdg/igittigitt/config.*` (+ `config.d/*`) | `/Library/Application Support/bitranox/igittigitt/config.*` |
|
|
19
|
+
| user | `~/.config/igittigitt/config.*` (+ `config.d/*`) | `~/Library/Application Support/bitranox/igittigitt/config.*` |
|
|
20
|
+
|
|
21
|
+
`.env` files (`SECTION__KEY=value`, found by searching upward from the working directory)
|
|
22
|
+
and environment variables (`IGITTIGITT___SECTION__KEY=value`) override the files. A single
|
|
23
|
+
value can be overridden per run with `--set SECTION.KEY=VALUE`.
|
|
24
|
+
|
|
25
|
+
Value coercion (in `.env` and the environment alike): an unquoted `true`/`false` -> bool,
|
|
26
|
+
`null`/`none` -> None, a number -> int/float only when it converts back to the same text (`007`
|
|
27
|
+
stays text), and a JSON array or object is parsed. Quote a `.env` value to keep it as text. A
|
|
28
|
+
list or table is that JSON written unquoted in `.env` (quoted for the shell in the environment),
|
|
29
|
+
or one key per entry (`LIB_LOG_RICH__SCRUB_PATTERNS__API_KEY=.+`); a comma-separated value is ONE
|
|
30
|
+
string, not a list.
|
|
31
|
+
|
|
32
|
+
A configuration file that cannot be loaded (invalid TOML, not UTF-8, unreadable) does not stop
|
|
33
|
+
every command. `config`, `check` and `filter`, which read the configuration, refuse with exit 78
|
|
34
|
+
and one line naming the file (`--traceback` adds the loader's traceback); `info`,
|
|
35
|
+
`config-deploy` (the command that replaces the broken file), `config-generate-examples` and
|
|
36
|
+
`--help` still run. A malformed or conflicting `--set` and an invalid `--profile` name are usage
|
|
37
|
+
errors (exit 2) for every command, whether or not the configuration loads.
|
|
38
|
+
|
|
39
|
+
## Settings reference
|
|
40
|
+
|
|
41
|
+
The authoritative, fully-commented reference is the set of bundled TOML files (one key per
|
|
42
|
+
documented block); `config-deploy` / `config-generate-examples` write them out:
|
|
43
|
+
|
|
44
|
+
- `defaultconfig.d/50-performance.toml` - the `[performance]` engine/CLI tuning knobs.
|
|
45
|
+
- `defaultconfig.d/90-logging.toml` - the `[lib_log_rich]` logging settings.
|
|
46
|
+
- `defaultconfig.d/40-layered-config.toml` - config-deploy permission defaults.
|
|
47
|
+
|
|
48
|
+
### `[performance]`
|
|
49
|
+
|
|
50
|
+
Only affects speed and cache memory, never which paths match. All defaults keep memory
|
|
51
|
+
bounded and were chosen by measurement.
|
|
52
|
+
|
|
53
|
+
| Key | Default | Meaning |
|
|
54
|
+
|---------------------|-----------|-----------------------------------------------------------------------------------------------------------------------|
|
|
55
|
+
| `dir_cache_max` | `8192` | Directory-decision LRU capacity per parser (`0` disables). Main speed-up on trees; memory `O(this)`, not `O(#files)`. |
|
|
56
|
+
| `pattern_cache_max` | `4096` | Process-wide compiled-regex cache capacity (keyed by distinct pattern). |
|
|
57
|
+
| `stdin_chunk_bytes` | `65536` | Stdin read granularity for the streaming commands. |
|
|
58
|
+
| `max_token_bytes` | `1048576` | Per-token safety bound; a separator-less token larger than this is rejected, not buffered unbounded. |
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
igittigitt --set performance.dir_cache_max=32768 filter -C repo
|
|
62
|
+
IGITTIGITT___PERFORMANCE__MAX_TOKEN_BYTES=4096 igittigitt check -C repo --stdin < paths
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### `[lib_layered_config.default_permissions]`
|
|
66
|
+
|
|
67
|
+
The Unix modes `config-deploy` gives the directories and files it writes (POSIX only), per layer:
|
|
68
|
+
`app_directory`/`app_file`, `host_directory`/`host_file`, `user_directory`/`user_file`, and
|
|
69
|
+
`enabled`. Built-in modes are 755/644 for app and host, 700/600 for user.
|
|
70
|
+
|
|
71
|
+
`config-deploy` decides none of this itself: it passes its options and any `--set` of this
|
|
72
|
+
section to lib_layered_config, which applies each target's own layer modes. The library reads the
|
|
73
|
+
section from the bundled defaults, the configuration files this deploy does not overwrite and the
|
|
74
|
+
environment, with the `--set` values laid over them. It never reads `.env` for it (neither one
|
|
75
|
+
found from the working directory nor an explicit `--env-file`), so a `.env` can neither change a
|
|
76
|
+
deployed mode nor block a deploy. `--dir-mode`/`--file-mode` override the configured modes for
|
|
77
|
+
every target. Without `--permissions`/`--no-permissions`, `enabled` decides; `enabled = false`
|
|
78
|
+
behaves like `--no-permissions`, and `--permissions` sets the modes anyway. `--no-permissions`
|
|
79
|
+
cannot be combined with a mode option (exit 2).
|
|
80
|
+
|
|
81
|
+
A mode is a plain octal STRING (`"0o750"`, `"750"`) inside 0..0o7777, never setuid/setgid/sticky,
|
|
82
|
+
group or world write, an execute bit on a file, or less than owner rwx (directory) / rw (file).
|
|
83
|
+
A bare integer is refused because TOML, the environment and `--set` all read `400` as the DECIMAL
|
|
84
|
+
number 400, which is `0o620`. Quote it in TOML (`user_file = "640"`); in an environment variable
|
|
85
|
+
or `--set` use the `0o` prefix (`0o640`). `enabled` must be a real boolean; `"no"`, `"off"`, `0`
|
|
86
|
+
or `1` are refused. An unknown key is refused rather than ignored. `--dir-mode`/`--file-mode`
|
|
87
|
+
follow the same rules and are refused as a usage error (exit 2). Any refused setting stops
|
|
88
|
+
`config-deploy` before it writes anything, with exit 78, one line per problem naming the key and
|
|
89
|
+
where it was set, and a hint, for example:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
Error: lib_layered_config.default_permissions.user_file: a bare integer is read as decimal (400 = 0o620); write the mode as an octal string: "0o640" (quoted) in a file, 0o640 in the environment or a runtime override (such as an application's --set) (source: env)
|
|
93
|
+
Hint: to deploy anyway, pass both --dir-mode and --file-mode (the built-in modes are 700 and 600 for user, 755 and 644 for app and host); --no-permissions also deploys, but leaves every mode to the umask, which can make a user file that holds secrets readable by other accounts.
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
A refused `--set` of the section names `(source: override)` and has no hint, since no option gets
|
|
97
|
+
past it: fix or drop the `--set`. The files a deploy writes are never read for it, so
|
|
98
|
+
`config-deploy --force` replaces a destination that carries a bad value, or does not parse at all,
|
|
99
|
+
without further options.
|
|
100
|
+
|
|
101
|
+
### `[lib_log_rich]`
|
|
102
|
+
|
|
103
|
+
Logging configuration (console level/theme, journald/eventlog/Graylog backends, queueing,
|
|
104
|
+
scrubbing, payload limits). Each key is documented inline in `90-logging.toml`. Common ones:
|
|
105
|
+
|
|
106
|
+
| Key | Example | Meaning |
|
|
107
|
+
|---------------------------------------|----------------------------|----------------------------------------|
|
|
108
|
+
| `console_level` | `DEBUG` | Minimum level shown on the console. |
|
|
109
|
+
| `console_theme` | `dark` | Console colour theme. |
|
|
110
|
+
| `enable_journald` | `true` | Also emit to systemd-journald (Linux). |
|
|
111
|
+
| `enable_graylog` / `graylog_endpoint` | `true` / `["host", 12201]` | Ship logs to Graylog (GELF). |
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
igittigitt --set lib_log_rich.console_level=DEBUG info
|
|
115
|
+
IGITTIGITT___LIB_LOG_RICH__CONSOLE_LEVEL=DEBUG igittigitt info
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
lib_log_rich also reads its own `LOG_*` variables. Logging takes those, and only those, from a
|
|
119
|
+
`.env`: from `--env-file` when given, otherwise from the nearest `.env` from the working directory
|
|
120
|
+
up to the project root, never replacing a variable that is already set. No other `.env` line
|
|
121
|
+
reaches the process environment.
|
|
122
|
+
|
|
123
|
+
A `[lib_log_rich]` value or `LOG_*` variable lib_log_rich refuses (a wrong type, its own range
|
|
124
|
+
checks such as `queue_maxsize = 0`, or an unknown level such as `LOG_CONSOLE_LEVEL=bogus`) is a
|
|
125
|
+
configuration failure like a broken file: logging starts with its defaults, `config`, `check` and
|
|
126
|
+
`filter` exit 78, and the other commands run. A refused `[lib_log_rich]` value leaves every valid
|
|
127
|
+
`LOG_*` variable in force; only a refused `LOG_*` variable makes logging start without all of them.
|
|
128
|
+
A problem the type check of the section finds gets one `Error:` line naming the key, never its
|
|
129
|
+
value; a value only lib_log_rich itself refuses is reported in lib_log_rich's own words
|
|
130
|
+
(`Error: lib_log_rich: Unknown log level: 'bogus'`), which may name neither the setting nor where
|
|
131
|
+
it was set.
|
|
@@ -95,7 +95,7 @@ make menu
|
|
|
95
95
|
|
|
96
96
|
## Running Integration Tests
|
|
97
97
|
|
|
98
|
-
Some tests require external resources
|
|
98
|
+
Some tests require external resources and are excluded from the default test run. These are marked with `@pytest.mark.local_only`.
|
|
99
99
|
|
|
100
100
|
### Quick Reference
|
|
101
101
|
|
|
@@ -105,39 +105,6 @@ Some tests require external resources (SMTP servers, databases) and are excluded
|
|
|
105
105
|
| `make test-slow` | ONLY `local_only` integration tests |
|
|
106
106
|
| `pytest tests/` | ALL tests (no marker filter) |
|
|
107
107
|
|
|
108
|
-
### Email Integration Tests
|
|
109
|
-
|
|
110
|
-
To run email tests that actually send messages:
|
|
111
|
-
|
|
112
|
-
1. **Create a `.env` file** in the project root with your SMTP settings:
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
# .env (copy from .env.example)
|
|
116
|
-
EMAIL__SMTP_HOSTS=smtp.example.com:587
|
|
117
|
-
EMAIL__FROM_ADDRESS=sender@example.com
|
|
118
|
-
EMAIL__RECIPIENTS=recipient@example.com
|
|
119
|
-
EMAIL__SMTP_USER=your_username
|
|
120
|
-
EMAIL__SMTP_PASSWORD=your_password
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Alternatively, use `--env-file` to point at an existing `.env` file:
|
|
124
|
-
|
|
125
|
-
```bash
|
|
126
|
-
igittigitt --env-file /path/to/my/.env send-notification --subject "Test" --message "Hello"
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
2. **Run the integration tests**:
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
make test-slow
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
3. **Or run specific email tests**:
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
|
-
pytest tests/test_cli_email_smtp.py -v
|
|
139
|
-
```
|
|
140
|
-
|
|
141
108
|
### Adding New Integration Tests
|
|
142
109
|
|
|
143
110
|
Mark tests that require external resources:
|