lsdsk 1.0.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.
- lsdsk-1.0.0/.claude-plugin/marketplace.json +16 -0
- lsdsk-1.0.0/.claude-plugin/plugin.json +11 -0
- lsdsk-1.0.0/.devcontainer/devcontainer.json +22 -0
- lsdsk-1.0.0/.devcontainer/settings.json +6 -0
- lsdsk-1.0.0/.env.example +366 -0
- lsdsk-1.0.0/.gitattributes +15 -0
- lsdsk-1.0.0/.github/actions/extract-metadata/action.yml +108 -0
- lsdsk-1.0.0/.github/dependabot.yml +22 -0
- lsdsk-1.0.0/.github/workflows/codeql.yml +39 -0
- lsdsk-1.0.0/.github/workflows/default_cicd_public.yml +436 -0
- lsdsk-1.0.0/.github/workflows/default_release_public.yml +95 -0
- lsdsk-1.0.0/.gitignore +208 -0
- lsdsk-1.0.0/.qlty/qlty.toml +2 -0
- lsdsk-1.0.0/.snyk +5 -0
- lsdsk-1.0.0/CHANGELOG.md +102 -0
- lsdsk-1.0.0/CONFIG.md +544 -0
- lsdsk-1.0.0/CONTRIBUTING.md +48 -0
- lsdsk-1.0.0/DEVELOPMENT.md +148 -0
- lsdsk-1.0.0/INSTALL.md +93 -0
- lsdsk-1.0.0/LICENSE +22 -0
- lsdsk-1.0.0/Makefile +453 -0
- lsdsk-1.0.0/PKG-INFO +466 -0
- lsdsk-1.0.0/README.md +413 -0
- lsdsk-1.0.0/SECURITY.md +40 -0
- lsdsk-1.0.0/ai-stance.md +90 -0
- lsdsk-1.0.0/ai-transparency.md +83 -0
- lsdsk-1.0.0/codecov.yml +27 -0
- lsdsk-1.0.0/docs/adr/0001-memory-adapters-in-src.md +29 -0
- lsdsk-1.0.0/docs/systemdesign/module_reference.md +192 -0
- lsdsk-1.0.0/notebooks/Quickstart.ipynb +257 -0
- lsdsk-1.0.0/pyproject.toml +326 -0
- lsdsk-1.0.0/skills/lsdsk/.skillwriter/checklist-2026-08-04.md +73 -0
- lsdsk-1.0.0/skills/lsdsk/.skillwriter/checklist-2026-08-05-default-report.md +48 -0
- lsdsk-1.0.0/skills/lsdsk/.skillwriter/checklist-2026-08-05-online-lookup.md +57 -0
- lsdsk-1.0.0/skills/lsdsk/.skillwriter/checklist-2026-08-05.md +57 -0
- lsdsk-1.0.0/skills/lsdsk/.skillwriter/checklist-2026-08-06-history-refusal-and-record.md +85 -0
- lsdsk-1.0.0/skills/lsdsk/.skillwriter/checklist-2026-08-06-isolated-review.md +109 -0
- lsdsk-1.0.0/skills/lsdsk/.skillwriter/checklist-2026-08-06-privileges-and-exit-codes.md +87 -0
- lsdsk-1.0.0/skills/lsdsk/SKILL.md +526 -0
- lsdsk-1.0.0/src/lsdsk/__init__.py +19 -0
- lsdsk-1.0.0/src/lsdsk/__init__conf__.py +90 -0
- lsdsk-1.0.0/src/lsdsk/__main__.py +9 -0
- lsdsk-1.0.0/src/lsdsk/adapters/__init__.py +14 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/__init__.py +80 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/commands/__init__.py +49 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/commands/config.py +452 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/commands/history.py +311 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/commands/info.py +72 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/commands/logging.py +34 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/commands/scan.py +632 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/constants.py +22 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/context.py +162 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/envelope.py +58 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/exit_codes.py +61 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/main.py +146 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/py.typed +0 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/root.py +235 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/safe_console.py +213 -0
- lsdsk-1.0.0/src/lsdsk/adapters/cli/typed_click.py +51 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/__init__.py +25 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/defaultconfig.d/40-layered-config.toml +71 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/defaultconfig.d/50-history.toml +65 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/defaultconfig.d/60-thresholds.toml +108 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/defaultconfig.d/70-display.toml +56 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/defaultconfig.d/90-logging.toml +432 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/defaultconfig.toml +46 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/deploy.py +122 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/display.py +72 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/history.py +99 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/loader.py +196 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/overrides.py +191 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/permissions.py +222 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/py.typed +0 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/secrets.py +124 -0
- lsdsk-1.0.0/src/lsdsk/adapters/config/tunables.py +173 -0
- lsdsk-1.0.0/src/lsdsk/adapters/history/__init__.py +27 -0
- lsdsk-1.0.0/src/lsdsk/adapters/history/store.py +306 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/__init__.py +19 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/decode/__init__.py +26 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/decode/ahci.py +130 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/decode/ata_identify.py +263 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/decode/ata_smart.py +276 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/decode/nvme.py +145 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/decode/pciids.py +225 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/decode/text.py +48 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/decode/virtualization.py +300 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/linux/__init__.py +12 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/linux/builder.py +647 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/linux/reader.py +759 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/snapshot.py +283 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/windows/__init__.py +15 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/windows/builder.py +346 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/windows/reader.py +658 -0
- lsdsk-1.0.0/src/lsdsk/adapters/hw/windows/winapi.py +613 -0
- lsdsk-1.0.0/src/lsdsk/adapters/logging/__init__.py +13 -0
- lsdsk-1.0.0/src/lsdsk/adapters/logging/py.typed +0 -0
- lsdsk-1.0.0/src/lsdsk/adapters/logging/setup.py +134 -0
- lsdsk-1.0.0/src/lsdsk/adapters/memory/__init__.py +57 -0
- lsdsk-1.0.0/src/lsdsk/adapters/memory/config.py +65 -0
- lsdsk-1.0.0/src/lsdsk/adapters/memory/history.py +74 -0
- lsdsk-1.0.0/src/lsdsk/adapters/memory/info.py +41 -0
- lsdsk-1.0.0/src/lsdsk/adapters/memory/logging.py +18 -0
- lsdsk-1.0.0/src/lsdsk/adapters/py.typed +0 -0
- lsdsk-1.0.0/src/lsdsk/adapters/render/__init__.py +11 -0
- lsdsk-1.0.0/src/lsdsk/adapters/render/full.py +114 -0
- lsdsk-1.0.0/src/lsdsk/adapters/render/layout.py +184 -0
- lsdsk-1.0.0/src/lsdsk/adapters/render/report.py +813 -0
- lsdsk-1.0.0/src/lsdsk/adapters/render/rows.py +17 -0
- lsdsk-1.0.0/src/lsdsk/adapters/render/tables.py +367 -0
- lsdsk-1.0.0/src/lsdsk/adapters/render/theme.py +385 -0
- lsdsk-1.0.0/src/lsdsk/adapters/render/trend.py +265 -0
- lsdsk-1.0.0/src/lsdsk/adapters/textfile.py +86 -0
- lsdsk-1.0.0/src/lsdsk/adapters/tui/__init__.py +10 -0
- lsdsk-1.0.0/src/lsdsk/adapters/tui/app.py +340 -0
- lsdsk-1.0.0/src/lsdsk/adapters/tui/typed_table.py +65 -0
- lsdsk-1.0.0/src/lsdsk/application/__init__.py +26 -0
- lsdsk-1.0.0/src/lsdsk/application/ports.py +101 -0
- lsdsk-1.0.0/src/lsdsk/application/py.typed +0 -0
- lsdsk-1.0.0/src/lsdsk/composition/__init__.py +113 -0
- lsdsk-1.0.0/src/lsdsk/composition/py.typed +0 -0
- lsdsk-1.0.0/src/lsdsk/domain/__init__.py +39 -0
- lsdsk-1.0.0/src/lsdsk/domain/diagnostics.py +1070 -0
- lsdsk-1.0.0/src/lsdsk/domain/enums.py +242 -0
- lsdsk-1.0.0/src/lsdsk/domain/errors.py +44 -0
- lsdsk-1.0.0/src/lsdsk/domain/history.py +663 -0
- lsdsk-1.0.0/src/lsdsk/domain/models.py +865 -0
- lsdsk-1.0.0/src/lsdsk/domain/py.typed +0 -0
- lsdsk-1.0.0/src/lsdsk/domain/thresholds.py +61 -0
- lsdsk-1.0.0/src/lsdsk/entry.py +26 -0
- lsdsk-1.0.0/src/lsdsk/py.typed +0 -0
- lsdsk-1.0.0/tests/conftest.py +638 -0
- lsdsk-1.0.0/tests/e2e/hostprobe.py +256 -0
- lsdsk-1.0.0/tests/e2e/test_hostprobe_logic.py +141 -0
- lsdsk-1.0.0/tests/e2e/test_real_hardware.py +241 -0
- lsdsk-1.0.0/tests/fixtures/hw/linux-minimal.json +1550 -0
- lsdsk-1.0.0/tests/fixtures/hw/linux-nvme-board.json +1384 -0
- lsdsk-1.0.0/tests/fixtures/hw/linux-sas-hba-later.json +2693 -0
- lsdsk-1.0.0/tests/fixtures/hw/linux-sas-hba.json +2680 -0
- lsdsk-1.0.0/tests/fixtures/hw/windows-ahci.json +328 -0
- lsdsk-1.0.0/tests/test_cache_effectiveness.py +147 -0
- lsdsk-1.0.0/tests/test_cli_config.py +722 -0
- lsdsk-1.0.0/tests/test_cli_core.py +231 -0
- lsdsk-1.0.0/tests/test_cli_env_file.py +170 -0
- lsdsk-1.0.0/tests/test_cli_exit_codes.py +155 -0
- lsdsk-1.0.0/tests/test_cli_overrides.py +167 -0
- lsdsk-1.0.0/tests/test_cli_scan.py +214 -0
- lsdsk-1.0.0/tests/test_cli_validation.py +138 -0
- lsdsk-1.0.0/tests/test_config_keys_are_live.py +270 -0
- lsdsk-1.0.0/tests/test_config_overrides.py +375 -0
- lsdsk-1.0.0/tests/test_data_architecture.py +222 -0
- lsdsk-1.0.0/tests/test_deploy_permissions.py +477 -0
- lsdsk-1.0.0/tests/test_diagnostics.py +696 -0
- lsdsk-1.0.0/tests/test_display.py +187 -0
- lsdsk-1.0.0/tests/test_enums.py +66 -0
- lsdsk-1.0.0/tests/test_environment.py +292 -0
- lsdsk-1.0.0/tests/test_errors.py +93 -0
- lsdsk-1.0.0/tests/test_history.py +360 -0
- lsdsk-1.0.0/tests/test_history_cli.py +606 -0
- lsdsk-1.0.0/tests/test_history_config.py +351 -0
- lsdsk-1.0.0/tests/test_history_diagnostics.py +198 -0
- lsdsk-1.0.0/tests/test_history_store.py +435 -0
- lsdsk-1.0.0/tests/test_hw_builder.py +302 -0
- lsdsk-1.0.0/tests/test_hw_decode.py +363 -0
- lsdsk-1.0.0/tests/test_input_bounds.py +279 -0
- lsdsk-1.0.0/tests/test_logging.py +31 -0
- lsdsk-1.0.0/tests/test_machine_drivable.py +202 -0
- lsdsk-1.0.0/tests/test_metadata.py +84 -0
- lsdsk-1.0.0/tests/test_metadata_sync.py +80 -0
- lsdsk-1.0.0/tests/test_module_entry.py +197 -0
- lsdsk-1.0.0/tests/test_module_reference.py +82 -0
- lsdsk-1.0.0/tests/test_no_subprocess_or_network.py +107 -0
- lsdsk-1.0.0/tests/test_platform_markers.py +62 -0
- lsdsk-1.0.0/tests/test_ports.py +74 -0
- lsdsk-1.0.0/tests/test_property_overrides.py +165 -0
- lsdsk-1.0.0/tests/test_safe_console.py +135 -0
- lsdsk-1.0.0/tests/test_slots.py +355 -0
- lsdsk-1.0.0/tests/test_terminal_width.py +135 -0
- lsdsk-1.0.0/tests/test_tui.py +431 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lsdsk",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "bitranox"
|
|
5
|
+
},
|
|
6
|
+
"metadata": {
|
|
7
|
+
"description": "lsdsk delivered as a single Claude Code plugin: a skill to inspect and diagnose storage topology, link speeds and disk health on Linux and Windows."
|
|
8
|
+
},
|
|
9
|
+
"plugins": [
|
|
10
|
+
{
|
|
11
|
+
"name": "lsdsk",
|
|
12
|
+
"source": ".",
|
|
13
|
+
"description": "Install and use lsdsk: group disks by controller, compare every link against what both ends could do, read SMART wear and error counters, and decide whether a slow link is a fault, a better slot, or a mainboard limit."
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lsdsk",
|
|
3
|
+
"description": "See which disks hang off which controller, whether each one runs at the speed it could, and how worn it is. Ships the lsdsk skill: install via uvx, read the topology tree and the problem summary, interpret every finding class, and tell an actionable fault from a mainboard ceiling that only a different board would lift.",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "bitranox"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://github.com/bitranox/lsdsk",
|
|
9
|
+
"repository": "https://github.com/bitranox/lsdsk",
|
|
10
|
+
"license": "MIT"
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"image": "mcr.microsoft.com/devcontainers/python:3.13",
|
|
3
|
+
|
|
4
|
+
"postCreateCommand": "bash -lc '\nPY=/usr/local/bin/python;\n$PY -m pip install -U pip && \\\n$PY -m pip install ipykernel && \\\n$PY -m pip install -e . && \\\n# register kernel using this exact interpreter\n$PY -m ipykernel install --name=python3 --display-name=\"Python 3 (3.13)\" --user && \\\n# patch the notebook to point to the python3 kernelspec (VS Code will bind to /usr/local/bin/python)\n$PY - <<\"PY\"\nimport json, pathlib\np = pathlib.Path(\"notebooks/Quickstart.ipynb\")\nif p.exists():\n nb = json.loads(p.read_text(encoding=\"utf-8\"))\n md = nb.setdefault(\"metadata\", {})\n md[\"kernelspec\"] = {\n \"name\": \"python3\",\n \"display_name\": \"Python 3 (3.13)\",\n \"language\": \"python\"\n }\n md[\"language_info\"] = {\"name\": \"python\"}\n p.write_text(json.dumps(nb, ensure_ascii=False, indent=1), encoding=\"utf-8\")\n print(\"Pinned kernelspec to python3 in\", p)\nelse:\n print(\"Notebook not found:\", p)\nPY'\n",
|
|
5
|
+
|
|
6
|
+
"customizations": {
|
|
7
|
+
"vscode": {
|
|
8
|
+
"extensions": ["ms-toolsai.jupyter", "ms-python.python"],
|
|
9
|
+
"settings": {
|
|
10
|
+
"workbench.startupEditor": "none",
|
|
11
|
+
"jupyter.alwaysTrustNotebooks": true,
|
|
12
|
+
"jupyter.kernelPickerType": "OnlyRecommended",
|
|
13
|
+
"python.defaultInterpreterPath": "/usr/local/bin/python"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"codespaces": {
|
|
17
|
+
"openFiles": ["README.md"]
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
"postAttachCommand": "bash -lc 'if [ -f notebooks/Quickstart.ipynb ]; then code -r notebooks/Quickstart.ipynb; fi'"
|
|
22
|
+
}
|
lsdsk-1.0.0/.env.example
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# Copy to .env and fill values as needed.
|
|
2
|
+
|
|
3
|
+
# Codecov upload token (required for private repos; optional for public repos)
|
|
4
|
+
CODECOV_TOKEN=
|
|
5
|
+
|
|
6
|
+
# PyPI API token for release workflow (.github/workflows/release.yml)
|
|
7
|
+
# Format: pypi-AgENdGVzdC5weXBpLm9yZwIk...
|
|
8
|
+
PYPI_API_TOKEN=
|
|
9
|
+
|
|
10
|
+
# Optional: GitHub token for API-limited tasks
|
|
11
|
+
GITHUB_TOKEN=
|
|
12
|
+
|
|
13
|
+
# ==============================================================================
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
#
|
|
16
|
+
# Naming Convention
|
|
17
|
+
# -----------------
|
|
18
|
+
# Format: <SECTION>__<KEY>=value (double underscore separates section from key)
|
|
19
|
+
# Lists use comma-separated values, booleans use true/false, floats use decimal notation.
|
|
20
|
+
#
|
|
21
|
+
# Full environment variable (outside .env):
|
|
22
|
+
# (triple underscore separates the application slug from the section)
|
|
23
|
+
|
|
24
|
+
# Type: comma-separated strings in "host[:port]" format
|
|
25
|
+
# Default: (empty, must be configured to send email)
|
|
26
|
+
|
|
27
|
+
# Purpose: Default sender address for outgoing emails
|
|
28
|
+
# Type: string (valid email address)
|
|
29
|
+
# Default: (empty, must be configured or passed via --from)
|
|
30
|
+
|
|
31
|
+
# Type: comma-separated strings (valid email addresses)
|
|
32
|
+
# Default: (empty, must be configured or passed via --to)
|
|
33
|
+
|
|
34
|
+
# Type: string
|
|
35
|
+
# Default: (empty, no authentication)
|
|
36
|
+
|
|
37
|
+
# Type: string
|
|
38
|
+
# Default: (empty, no authentication)
|
|
39
|
+
|
|
40
|
+
# Purpose: Enable STARTTLS negotiation before authentication
|
|
41
|
+
# Type: boolean (true/false)
|
|
42
|
+
# Default: true
|
|
43
|
+
|
|
44
|
+
# Type: float
|
|
45
|
+
# Default: 30.0
|
|
46
|
+
|
|
47
|
+
# Type: boolean (true/false)
|
|
48
|
+
# Default: true
|
|
49
|
+
|
|
50
|
+
# Type: boolean (true/false)
|
|
51
|
+
# Default: true
|
|
52
|
+
|
|
53
|
+
# ==============================================================================
|
|
54
|
+
# Logging Configuration (lib_log_rich)
|
|
55
|
+
# ==============================================================================
|
|
56
|
+
#
|
|
57
|
+
# Naming Convention
|
|
58
|
+
# -----------------
|
|
59
|
+
# Format: <SECTION>__<KEY>=value (double underscore separates section from key)
|
|
60
|
+
# Strings need no quotes. Booleans: true/false. Floats: 1.0. Lists: comma-separated.
|
|
61
|
+
#
|
|
62
|
+
# Full environment variable (outside .env):
|
|
63
|
+
# LSDSK___LIB_LOG_RICH__<KEY>=value
|
|
64
|
+
#
|
|
65
|
+
# For nested subsections (e.g. payload_limits):
|
|
66
|
+
# LSDSK___LIB_LOG_RICH__PAYLOAD_LIMITS__<KEY>=value
|
|
67
|
+
# .env: LIB_LOG_RICH__PAYLOAD_LIMITS__<KEY>=value
|
|
68
|
+
|
|
69
|
+
# --- Core Identity ---
|
|
70
|
+
|
|
71
|
+
# Purpose: Logical service name recorded in each log event
|
|
72
|
+
# Type: string (non-empty)
|
|
73
|
+
# Default: "lsdsk"
|
|
74
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__SERVICE=my-application
|
|
75
|
+
# .env: LIB_LOG_RICH__SERVICE=my-application
|
|
76
|
+
# LIB_LOG_RICH__SERVICE=my-application
|
|
77
|
+
|
|
78
|
+
# Purpose: Deployment environment label
|
|
79
|
+
# Type: string (non-empty)
|
|
80
|
+
# Default: "prod"
|
|
81
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__ENVIRONMENT=dev
|
|
82
|
+
# .env: LIB_LOG_RICH__ENVIRONMENT=dev
|
|
83
|
+
# LIB_LOG_RICH__ENVIRONMENT=dev
|
|
84
|
+
|
|
85
|
+
# --- Console Output ---
|
|
86
|
+
|
|
87
|
+
# Purpose: Minimum severity level emitted to Rich console
|
|
88
|
+
# Type: string (case-insensitive): "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
|
|
89
|
+
# Default: "INFO"
|
|
90
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__CONSOLE_LEVEL=DEBUG
|
|
91
|
+
# .env: LIB_LOG_RICH__CONSOLE_LEVEL=DEBUG
|
|
92
|
+
# LIB_LOG_RICH__CONSOLE_LEVEL=DEBUG
|
|
93
|
+
|
|
94
|
+
# Purpose: Where to send console output
|
|
95
|
+
# Type: string: "stdout", "stderr", "both", "custom", "none"
|
|
96
|
+
# Default: "stderr"
|
|
97
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__CONSOLE_STREAM=stderr
|
|
98
|
+
# .env: LIB_LOG_RICH__CONSOLE_STREAM=stderr
|
|
99
|
+
# LIB_LOG_RICH__CONSOLE_STREAM=stderr
|
|
100
|
+
|
|
101
|
+
# Purpose: Console line layout preset
|
|
102
|
+
# Type: string (case-insensitive): "full", "short", "full_loc", "short_loc"
|
|
103
|
+
# Default: "short_loc"
|
|
104
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__CONSOLE_FORMAT_PRESET=short_loc
|
|
105
|
+
# .env: LIB_LOG_RICH__CONSOLE_FORMAT_PRESET=short_loc
|
|
106
|
+
# LIB_LOG_RICH__CONSOLE_FORMAT_PRESET=short_loc
|
|
107
|
+
|
|
108
|
+
# Purpose: Custom console output template (overrides preset)
|
|
109
|
+
# Type: string (Python str.format template); empty = uses preset
|
|
110
|
+
# Default: (empty, uses preset)
|
|
111
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__CONSOLE_FORMAT_TEMPLATE={timestamp_trimmed_naive_loc} {level_code} {message}
|
|
112
|
+
# .env: LIB_LOG_RICH__CONSOLE_FORMAT_TEMPLATE={timestamp_trimmed_naive_loc} {level_code} {message}
|
|
113
|
+
# LIB_LOG_RICH__CONSOLE_FORMAT_TEMPLATE={timestamp_trimmed_naive_loc} {level_code} {message}
|
|
114
|
+
|
|
115
|
+
# Purpose: Rich color palette for console output
|
|
116
|
+
# Type: string: "classic", "dark", "neon", "pastel"
|
|
117
|
+
# Default: "dark"
|
|
118
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__CONSOLE_THEME=dark
|
|
119
|
+
# .env: LIB_LOG_RICH__CONSOLE_THEME=dark
|
|
120
|
+
# LIB_LOG_RICH__CONSOLE_THEME=dark
|
|
121
|
+
|
|
122
|
+
# Purpose: Override Rich styles per severity level (comma-separated LEVEL=style pairs)
|
|
123
|
+
# Type: comma-separated LEVEL=style pairs
|
|
124
|
+
# Default: (empty, uses theme defaults)
|
|
125
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__CONSOLE_STYLES=DEBUG=dim,ERROR=bold red
|
|
126
|
+
# .env: LIB_LOG_RICH__CONSOLE_STYLES=DEBUG=dim,ERROR=bold red
|
|
127
|
+
# LIB_LOG_RICH__CONSOLE_STYLES=DEBUG=dim,ERROR=bold red
|
|
128
|
+
|
|
129
|
+
# Purpose: Force ANSI color output even when stderr is not a TTY
|
|
130
|
+
# Type: boolean (true/false)
|
|
131
|
+
# Default: false
|
|
132
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__FORCE_COLOR=true
|
|
133
|
+
# .env: LIB_LOG_RICH__FORCE_COLOR=true
|
|
134
|
+
# LIB_LOG_RICH__FORCE_COLOR=true
|
|
135
|
+
|
|
136
|
+
# Purpose: Disable color output entirely (overrides force_color)
|
|
137
|
+
# Type: boolean (true/false)
|
|
138
|
+
# Default: false
|
|
139
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__NO_COLOR=false
|
|
140
|
+
# .env: LIB_LOG_RICH__NO_COLOR=false
|
|
141
|
+
# LIB_LOG_RICH__NO_COLOR=false
|
|
142
|
+
|
|
143
|
+
# --- Backend Adapters ---
|
|
144
|
+
|
|
145
|
+
# Purpose: Minimum severity threshold for journald and Windows Event Log
|
|
146
|
+
# Type: string (case-insensitive): "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
|
|
147
|
+
# Default: "WARNING"
|
|
148
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__BACKEND_LEVEL=WARNING
|
|
149
|
+
# .env: LIB_LOG_RICH__BACKEND_LEVEL=WARNING
|
|
150
|
+
# LIB_LOG_RICH__BACKEND_LEVEL=WARNING
|
|
151
|
+
|
|
152
|
+
# Purpose: Enable systemd journald adapter (Linux only; requires python3-systemd)
|
|
153
|
+
# Type: boolean (true/false)
|
|
154
|
+
# Default: false
|
|
155
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__ENABLE_JOURNALD=true
|
|
156
|
+
# .env: LIB_LOG_RICH__ENABLE_JOURNALD=true
|
|
157
|
+
# LIB_LOG_RICH__ENABLE_JOURNALD=true
|
|
158
|
+
|
|
159
|
+
# Purpose: Enable Windows Event Log adapter (Windows only)
|
|
160
|
+
# Type: boolean (true/false)
|
|
161
|
+
# Default: false
|
|
162
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__ENABLE_EVENTLOG=false
|
|
163
|
+
# .env: LIB_LOG_RICH__ENABLE_EVENTLOG=false
|
|
164
|
+
# LIB_LOG_RICH__ENABLE_EVENTLOG=false
|
|
165
|
+
|
|
166
|
+
# --- Graylog / GELF ---
|
|
167
|
+
|
|
168
|
+
# Purpose: Enable Graylog adapter for centralized logging
|
|
169
|
+
# Type: boolean (true/false)
|
|
170
|
+
# Default: false
|
|
171
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__ENABLE_GRAYLOG=true
|
|
172
|
+
# .env: LIB_LOG_RICH__ENABLE_GRAYLOG=true
|
|
173
|
+
# LIB_LOG_RICH__ENABLE_GRAYLOG=true
|
|
174
|
+
|
|
175
|
+
# Purpose: Graylog server address (host:port)
|
|
176
|
+
# Type: string "host:port"
|
|
177
|
+
# Default: (none, must be configured to enable Graylog)
|
|
178
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__GRAYLOG_ENDPOINT=graylog.example.com:12201
|
|
179
|
+
# .env: LIB_LOG_RICH__GRAYLOG_ENDPOINT=graylog.example.com:12201
|
|
180
|
+
# LIB_LOG_RICH__GRAYLOG_ENDPOINT=graylog.example.com:12201
|
|
181
|
+
|
|
182
|
+
# Purpose: Transport protocol for Graylog (tcp, udp); TLS only supported with tcp
|
|
183
|
+
# Type: string: "tcp", "udp"
|
|
184
|
+
# Default: "tcp"
|
|
185
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__GRAYLOG_PROTOCOL=tcp
|
|
186
|
+
# .env: LIB_LOG_RICH__GRAYLOG_PROTOCOL=tcp
|
|
187
|
+
# LIB_LOG_RICH__GRAYLOG_PROTOCOL=tcp
|
|
188
|
+
|
|
189
|
+
# Purpose: Wrap Graylog TCP connection in TLS
|
|
190
|
+
# Type: boolean (true/false)
|
|
191
|
+
# Default: false
|
|
192
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__GRAYLOG_TLS=true
|
|
193
|
+
# .env: LIB_LOG_RICH__GRAYLOG_TLS=true
|
|
194
|
+
# LIB_LOG_RICH__GRAYLOG_TLS=true
|
|
195
|
+
|
|
196
|
+
# Purpose: Minimum severity level sent to Graylog
|
|
197
|
+
# Type: string (case-insensitive): "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
|
|
198
|
+
# Default: "WARNING"
|
|
199
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__GRAYLOG_LEVEL=WARNING
|
|
200
|
+
# .env: LIB_LOG_RICH__GRAYLOG_LEVEL=WARNING
|
|
201
|
+
# LIB_LOG_RICH__GRAYLOG_LEVEL=WARNING
|
|
202
|
+
|
|
203
|
+
# --- Queue Settings ---
|
|
204
|
+
|
|
205
|
+
# Purpose: Route log events through background queue worker (false = synchronous, blocks on I/O)
|
|
206
|
+
# Type: boolean (true/false)
|
|
207
|
+
# Default: true
|
|
208
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__QUEUE_ENABLED=true
|
|
209
|
+
# .env: LIB_LOG_RICH__QUEUE_ENABLED=true
|
|
210
|
+
# LIB_LOG_RICH__QUEUE_ENABLED=true
|
|
211
|
+
|
|
212
|
+
# Purpose: Maximum pending events before queue full policy applies
|
|
213
|
+
# Type: integer (> 0)
|
|
214
|
+
# Default: 2048
|
|
215
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__QUEUE_MAXSIZE=2048
|
|
216
|
+
# .env: LIB_LOG_RICH__QUEUE_MAXSIZE=2048
|
|
217
|
+
# LIB_LOG_RICH__QUEUE_MAXSIZE=2048
|
|
218
|
+
|
|
219
|
+
# Purpose: Behavior when queue is full
|
|
220
|
+
# Type: string: "block" (wait for space), "drop" (discard events)
|
|
221
|
+
# Default: "block"
|
|
222
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__QUEUE_FULL_POLICY=block
|
|
223
|
+
# .env: LIB_LOG_RICH__QUEUE_FULL_POLICY=block
|
|
224
|
+
# LIB_LOG_RICH__QUEUE_FULL_POLICY=block
|
|
225
|
+
|
|
226
|
+
# Purpose: Timeout in seconds for blocking queue puts (0 = infinite wait)
|
|
227
|
+
# Type: float (>= 0)
|
|
228
|
+
# Default: 1.0
|
|
229
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__QUEUE_PUT_TIMEOUT=1.0
|
|
230
|
+
# .env: LIB_LOG_RICH__QUEUE_PUT_TIMEOUT=1.0
|
|
231
|
+
# LIB_LOG_RICH__QUEUE_PUT_TIMEOUT=1.0
|
|
232
|
+
|
|
233
|
+
# Purpose: Maximum wait in seconds for queue drain during shutdown
|
|
234
|
+
# Type: float (> 0)
|
|
235
|
+
# Default: 5.0
|
|
236
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__QUEUE_STOP_TIMEOUT=5.0
|
|
237
|
+
# .env: LIB_LOG_RICH__QUEUE_STOP_TIMEOUT=5.0
|
|
238
|
+
# LIB_LOG_RICH__QUEUE_STOP_TIMEOUT=5.0
|
|
239
|
+
|
|
240
|
+
# --- Ring Buffer ---
|
|
241
|
+
|
|
242
|
+
# Purpose: Enable in-memory circular buffer for log history (enables log.dump())
|
|
243
|
+
# Type: boolean (true/false)
|
|
244
|
+
# Default: true
|
|
245
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__ENABLE_RING_BUFFER=true
|
|
246
|
+
# .env: LIB_LOG_RICH__ENABLE_RING_BUFFER=true
|
|
247
|
+
# LIB_LOG_RICH__ENABLE_RING_BUFFER=true
|
|
248
|
+
|
|
249
|
+
# Purpose: Maximum events retained in ring buffer
|
|
250
|
+
# Type: integer (> 0)
|
|
251
|
+
# Default: 25000
|
|
252
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__RING_BUFFER_SIZE=25000
|
|
253
|
+
# .env: LIB_LOG_RICH__RING_BUFFER_SIZE=25000
|
|
254
|
+
# LIB_LOG_RICH__RING_BUFFER_SIZE=25000
|
|
255
|
+
|
|
256
|
+
# --- Dump Format ---
|
|
257
|
+
|
|
258
|
+
# Purpose: Default layout for text dumps
|
|
259
|
+
# Type: string (case-insensitive): "full", "short", "full_loc", "short_loc"
|
|
260
|
+
# Default: "full"
|
|
261
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__DUMP_FORMAT_PRESET=full
|
|
262
|
+
# .env: LIB_LOG_RICH__DUMP_FORMAT_PRESET=full
|
|
263
|
+
# LIB_LOG_RICH__DUMP_FORMAT_PRESET=full
|
|
264
|
+
|
|
265
|
+
# Purpose: Custom template for text dumps (overrides preset)
|
|
266
|
+
# Type: string (Python str.format template); empty = uses preset
|
|
267
|
+
# Default: (empty, uses preset)
|
|
268
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__DUMP_FORMAT_TEMPLATE={timestamp_trimmed_naive_loc} {level_code} {message}
|
|
269
|
+
# .env: LIB_LOG_RICH__DUMP_FORMAT_TEMPLATE={timestamp_trimmed_naive_loc} {level_code} {message}
|
|
270
|
+
# LIB_LOG_RICH__DUMP_FORMAT_TEMPLATE={timestamp_trimmed_naive_loc} {level_code} {message}
|
|
271
|
+
|
|
272
|
+
# --- Security & Payload ---
|
|
273
|
+
|
|
274
|
+
# Purpose: Regex patterns to redact sensitive data (comma-separated field=regex pairs)
|
|
275
|
+
# Type: comma-separated field=regex pairs
|
|
276
|
+
# Default: password=.+,secret=.+,token=.+
|
|
277
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__SCRUB_PATTERNS=password=.+,secret=.+,token=.+,api_key=.+
|
|
278
|
+
# .env: LIB_LOG_RICH__SCRUB_PATTERNS=password=.+,secret=.+,token=.+,api_key=.+
|
|
279
|
+
# LIB_LOG_RICH__SCRUB_PATTERNS=password=.+,secret=.+,token=.+,api_key=.+
|
|
280
|
+
|
|
281
|
+
# Purpose: Throttle log emissions (MAX_EVENTS:WINDOW_SECONDS)
|
|
282
|
+
# Type: string "max_events:window_seconds"
|
|
283
|
+
# Default: (none, no rate limiting)
|
|
284
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__RATE_LIMIT=100:60
|
|
285
|
+
# .env: LIB_LOG_RICH__RATE_LIMIT=100:60
|
|
286
|
+
# LIB_LOG_RICH__RATE_LIMIT=100:60
|
|
287
|
+
|
|
288
|
+
# --- Payload Limits ---
|
|
289
|
+
|
|
290
|
+
# Purpose: Truncate vs reject oversized messages
|
|
291
|
+
# Type: boolean (true/false)
|
|
292
|
+
# Default: true
|
|
293
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__PAYLOAD_LIMITS__TRUNCATE_MESSAGE=true
|
|
294
|
+
# .env: LIB_LOG_RICH__PAYLOAD_LIMITS__TRUNCATE_MESSAGE=true
|
|
295
|
+
# LIB_LOG_RICH__PAYLOAD_LIMITS__TRUNCATE_MESSAGE=true
|
|
296
|
+
|
|
297
|
+
# Purpose: Maximum characters in primary log message
|
|
298
|
+
# Type: integer
|
|
299
|
+
# Default: 4096
|
|
300
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__PAYLOAD_LIMITS__MESSAGE_MAX_CHARS=4096
|
|
301
|
+
# .env: LIB_LOG_RICH__PAYLOAD_LIMITS__MESSAGE_MAX_CHARS=4096
|
|
302
|
+
# LIB_LOG_RICH__PAYLOAD_LIMITS__MESSAGE_MAX_CHARS=4096
|
|
303
|
+
|
|
304
|
+
# Purpose: Maximum keys in extra metadata dict
|
|
305
|
+
# Type: integer
|
|
306
|
+
# Default: 25
|
|
307
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__PAYLOAD_LIMITS__EXTRA_MAX_KEYS=25
|
|
308
|
+
# .env: LIB_LOG_RICH__PAYLOAD_LIMITS__EXTRA_MAX_KEYS=25
|
|
309
|
+
# LIB_LOG_RICH__PAYLOAD_LIMITS__EXTRA_MAX_KEYS=25
|
|
310
|
+
|
|
311
|
+
# Purpose: Maximum characters per extra value (after stringify)
|
|
312
|
+
# Type: integer
|
|
313
|
+
# Default: 512
|
|
314
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__PAYLOAD_LIMITS__EXTRA_MAX_VALUE_CHARS=512
|
|
315
|
+
# .env: LIB_LOG_RICH__PAYLOAD_LIMITS__EXTRA_MAX_VALUE_CHARS=512
|
|
316
|
+
# LIB_LOG_RICH__PAYLOAD_LIMITS__EXTRA_MAX_VALUE_CHARS=512
|
|
317
|
+
|
|
318
|
+
# Purpose: Maximum nesting depth for extra structures
|
|
319
|
+
# Type: integer
|
|
320
|
+
# Default: 3
|
|
321
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__PAYLOAD_LIMITS__EXTRA_MAX_DEPTH=3
|
|
322
|
+
# .env: LIB_LOG_RICH__PAYLOAD_LIMITS__EXTRA_MAX_DEPTH=3
|
|
323
|
+
# LIB_LOG_RICH__PAYLOAD_LIMITS__EXTRA_MAX_DEPTH=3
|
|
324
|
+
|
|
325
|
+
# Purpose: Total UTF-8 byte limit for sanitized extra payload
|
|
326
|
+
# Type: integer
|
|
327
|
+
# Default: 8192
|
|
328
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__PAYLOAD_LIMITS__EXTRA_MAX_TOTAL_BYTES=8192
|
|
329
|
+
# .env: LIB_LOG_RICH__PAYLOAD_LIMITS__EXTRA_MAX_TOTAL_BYTES=8192
|
|
330
|
+
# LIB_LOG_RICH__PAYLOAD_LIMITS__EXTRA_MAX_TOTAL_BYTES=8192
|
|
331
|
+
|
|
332
|
+
# Purpose: Maximum keys in LogContext.extra
|
|
333
|
+
# Type: integer
|
|
334
|
+
# Default: 20
|
|
335
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__PAYLOAD_LIMITS__CONTEXT_MAX_KEYS=20
|
|
336
|
+
# .env: LIB_LOG_RICH__PAYLOAD_LIMITS__CONTEXT_MAX_KEYS=20
|
|
337
|
+
# LIB_LOG_RICH__PAYLOAD_LIMITS__CONTEXT_MAX_KEYS=20
|
|
338
|
+
|
|
339
|
+
# Purpose: Maximum characters per context value
|
|
340
|
+
# Type: integer
|
|
341
|
+
# Default: 256
|
|
342
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__PAYLOAD_LIMITS__CONTEXT_MAX_VALUE_CHARS=256
|
|
343
|
+
# .env: LIB_LOG_RICH__PAYLOAD_LIMITS__CONTEXT_MAX_VALUE_CHARS=256
|
|
344
|
+
# LIB_LOG_RICH__PAYLOAD_LIMITS__CONTEXT_MAX_VALUE_CHARS=256
|
|
345
|
+
|
|
346
|
+
# Purpose: Number of frames preserved in exception tracebacks
|
|
347
|
+
# Type: integer
|
|
348
|
+
# Default: 10
|
|
349
|
+
# Environment Variable: LSDSK___LIB_LOG_RICH__PAYLOAD_LIMITS__STACKTRACE_MAX_FRAMES=10
|
|
350
|
+
# .env: LIB_LOG_RICH__PAYLOAD_LIMITS__STACKTRACE_MAX_FRAMES=10
|
|
351
|
+
# LIB_LOG_RICH__PAYLOAD_LIMITS__STACKTRACE_MAX_FRAMES=10
|
|
352
|
+
|
|
353
|
+
# ---------------------------------------------------------------------------
|
|
354
|
+
# Real-hardware end-to-end tests (make testintegration)
|
|
355
|
+
# ---------------------------------------------------------------------------
|
|
356
|
+
#
|
|
357
|
+
# Which machines `tests/e2e/test_real_hardware.py` ships the probe to. Left
|
|
358
|
+
# empty here and set in .env, which is never committed: a fleet's hostnames are
|
|
359
|
+
# infrastructure, and this repository is public.
|
|
360
|
+
#
|
|
361
|
+
# Format: comma-separated host:user:family, family being linux or windows.
|
|
362
|
+
# Example: LSDSK_E2E_HOSTS="server-a:root:linux,workstation-b:admin:windows"
|
|
363
|
+
#
|
|
364
|
+
# Unset, the hardware tests skip and say so. Authentication uses the ssh key at
|
|
365
|
+
# ~/.ssh/, never a password in this file.
|
|
366
|
+
LSDSK_E2E_HOSTS=
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Normalize every text file to LF in the repo AND the working tree, on every OS.
|
|
2
|
+
# `text=auto` auto-detects binary and leaves it untouched; `eol=lf` forces LF checkout
|
|
3
|
+
# so a Windows edit/save cannot reintroduce CRLF churn.
|
|
4
|
+
* text=auto eol=lf
|
|
5
|
+
|
|
6
|
+
# Runtime-critical: CRLF here breaks execution on Linux (^M in shebang / make recipes).
|
|
7
|
+
Makefile text eol=lf
|
|
8
|
+
*.sh text eol=lf
|
|
9
|
+
*.py text eol=lf
|
|
10
|
+
*.ps1 text eol=lf
|
|
11
|
+
*.toml text eol=lf
|
|
12
|
+
*.md text eol=lf
|
|
13
|
+
*.json text eol=lf
|
|
14
|
+
*.yml text eol=lf
|
|
15
|
+
*.yaml text eol=lf
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
name: Extract project metadata
|
|
2
|
+
description: >-
|
|
3
|
+
Parse pyproject.toml and expose project metadata as step outputs.
|
|
4
|
+
Requires Python to be available in the runner environment.
|
|
5
|
+
Auto-installs rtoml if not already present.
|
|
6
|
+
|
|
7
|
+
outputs:
|
|
8
|
+
PACKAGE_MODULE:
|
|
9
|
+
description: Python-importable module name (underscored)
|
|
10
|
+
value: ${{ steps.meta.outputs.PACKAGE_MODULE }}
|
|
11
|
+
PROJECT_NAME:
|
|
12
|
+
description: Project name from pyproject.toml
|
|
13
|
+
value: ${{ steps.meta.outputs.PROJECT_NAME }}
|
|
14
|
+
CLI_BIN:
|
|
15
|
+
description: CLI entry-point binary name
|
|
16
|
+
value: ${{ steps.meta.outputs.CLI_BIN }}
|
|
17
|
+
SRC_PATH:
|
|
18
|
+
description: Source directory path (e.g. "src")
|
|
19
|
+
value: ${{ steps.meta.outputs.SRC_PATH }}
|
|
20
|
+
COV_FAIL_UNDER:
|
|
21
|
+
description: Minimum coverage percentage from tool.coverage.report.fail_under
|
|
22
|
+
value: ${{ steps.meta.outputs.COV_FAIL_UNDER }}
|
|
23
|
+
COV_REPORT_FILE:
|
|
24
|
+
description: Coverage report filename (e.g. "coverage.xml")
|
|
25
|
+
value: ${{ steps.meta.outputs.COV_REPORT_FILE }}
|
|
26
|
+
PYTEST_VERBOSITY:
|
|
27
|
+
description: Pytest verbosity flag (e.g. "-vv")
|
|
28
|
+
value: ${{ steps.meta.outputs.PYTEST_VERBOSITY }}
|
|
29
|
+
BANDIT_SKIP_FLAG:
|
|
30
|
+
description: Bandit skip flag string (e.g. "-s B101,B601") or empty
|
|
31
|
+
value: ${{ steps.meta.outputs.BANDIT_SKIP_FLAG }}
|
|
32
|
+
PYTEST_MARKER_EXPR:
|
|
33
|
+
description: >-
|
|
34
|
+
Marker expression for the CI test run (e.g. "not local_only and not integration"),
|
|
35
|
+
built from tool.ci.pytest-exclude-markers
|
|
36
|
+
value: ${{ steps.meta.outputs.PYTEST_MARKER_EXPR }}
|
|
37
|
+
|
|
38
|
+
runs:
|
|
39
|
+
using: composite
|
|
40
|
+
steps:
|
|
41
|
+
- name: Extract project metadata from pyproject.toml
|
|
42
|
+
id: meta
|
|
43
|
+
shell: python
|
|
44
|
+
run: |
|
|
45
|
+
import os, subprocess, sys
|
|
46
|
+
try:
|
|
47
|
+
import rtoml
|
|
48
|
+
except ModuleNotFoundError:
|
|
49
|
+
subprocess.check_call([sys.executable, "-m", "pip", "install", "rtoml", "-q"])
|
|
50
|
+
import rtoml
|
|
51
|
+
|
|
52
|
+
from pathlib import Path
|
|
53
|
+
data = rtoml.load(Path('pyproject.toml'))
|
|
54
|
+
|
|
55
|
+
# --- [project] section ---
|
|
56
|
+
project = data['project']['name']
|
|
57
|
+
module = project.replace('-', '_')
|
|
58
|
+
dash = project.replace('_', '-')
|
|
59
|
+
scripts = list(data['project'].get('scripts', {}).keys())
|
|
60
|
+
cli_bin = scripts[0] if scripts else dash
|
|
61
|
+
|
|
62
|
+
# --- [tool.scripts.test] section ---
|
|
63
|
+
scripts_test = data.get('tool', {}).get('scripts', {}).get('test', {})
|
|
64
|
+
src_path = scripts_test.get('src-path', 'src')
|
|
65
|
+
pytest_verbosity = scripts_test.get('pytest-verbosity', '-vv')
|
|
66
|
+
cov_report_file = scripts_test.get('coverage-report-file', 'coverage.xml')
|
|
67
|
+
|
|
68
|
+
# --- [tool.coverage.report] section ---
|
|
69
|
+
cov_fail_under = (
|
|
70
|
+
data.get('tool', {}).get('coverage', {}).get('report', {}).get('fail_under', 90)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# --- [tool.bandit] section ---
|
|
74
|
+
bandit_skips = data.get('tool', {}).get('bandit', {}).get('skips', [])
|
|
75
|
+
bandit_skip_flag = f"-s {','.join(bandit_skips)}" if bandit_skips else ""
|
|
76
|
+
|
|
77
|
+
# --- [tool.ci] section ---
|
|
78
|
+
# CI always skips local_only (those tests need a dev box). A project whose
|
|
79
|
+
# suite also carries markers CI cannot satisfy - integration tests needing a
|
|
80
|
+
# container that a separate workflow starts - names them here, rather than
|
|
81
|
+
# every such repo forking this workflow to edit one -m expression.
|
|
82
|
+
ci_config = data.get('tool', {}).get('ci', {})
|
|
83
|
+
exclude_markers = ['local_only', *ci_config.get('pytest-exclude-markers', [])]
|
|
84
|
+
pytest_marker_expr = ' and '.join(f'not {m}' for m in exclude_markers)
|
|
85
|
+
|
|
86
|
+
# Write to GITHUB_OUTPUT for composite action outputs
|
|
87
|
+
with open(os.environ['GITHUB_OUTPUT'], 'a', encoding='utf-8') as out:
|
|
88
|
+
out.write(f"PROJECT_NAME={project}\n")
|
|
89
|
+
out.write(f"PACKAGE_MODULE={module}\n")
|
|
90
|
+
out.write(f"CLI_BIN={cli_bin}\n")
|
|
91
|
+
out.write(f"SRC_PATH={src_path}\n")
|
|
92
|
+
out.write(f"PYTEST_VERBOSITY={pytest_verbosity}\n")
|
|
93
|
+
out.write(f"COV_REPORT_FILE={cov_report_file}\n")
|
|
94
|
+
out.write(f"COV_FAIL_UNDER={cov_fail_under}\n")
|
|
95
|
+
out.write(f"BANDIT_SKIP_FLAG={bandit_skip_flag}\n")
|
|
96
|
+
out.write(f"PYTEST_MARKER_EXPR={pytest_marker_expr}\n")
|
|
97
|
+
|
|
98
|
+
# Also write to GITHUB_ENV so subsequent steps can use ${{ env.VAR }}
|
|
99
|
+
with open(os.environ['GITHUB_ENV'], 'a', encoding='utf-8') as env:
|
|
100
|
+
env.write(f"PROJECT_NAME={project}\n")
|
|
101
|
+
env.write(f"PACKAGE_MODULE={module}\n")
|
|
102
|
+
env.write(f"CLI_BIN={cli_bin}\n")
|
|
103
|
+
env.write(f"SRC_PATH={src_path}\n")
|
|
104
|
+
env.write(f"PYTEST_VERBOSITY={pytest_verbosity}\n")
|
|
105
|
+
env.write(f"COV_REPORT_FILE={cov_report_file}\n")
|
|
106
|
+
env.write(f"COV_FAIL_UNDER={cov_fail_under}\n")
|
|
107
|
+
env.write(f"BANDIT_SKIP_FLAG={bandit_skip_flag}\n")
|
|
108
|
+
env.write(f"PYTEST_MARKER_EXPR={pytest_marker_expr}\n")
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "pip"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
allow:
|
|
8
|
+
- dependency-type: "direct"
|
|
9
|
+
labels:
|
|
10
|
+
- "dependencies"
|
|
11
|
+
commit-message:
|
|
12
|
+
prefix: "deps"
|
|
13
|
+
include: "scope"
|
|
14
|
+
- package-ecosystem: "github-actions"
|
|
15
|
+
directory: "/"
|
|
16
|
+
schedule:
|
|
17
|
+
interval: "weekly"
|
|
18
|
+
labels:
|
|
19
|
+
- "dependencies"
|
|
20
|
+
commit-message:
|
|
21
|
+
prefix: "deps"
|
|
22
|
+
include: "scope"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, master ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main, master ]
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: '0 8 * * 1'
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
analyze:
|
|
13
|
+
name: CodeQL Analyze
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
actions: read
|
|
17
|
+
contents: read
|
|
18
|
+
security-events: write
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
matrix:
|
|
22
|
+
language: [ 'python' ]
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- name: Checkout repository
|
|
26
|
+
uses: actions/checkout@v7
|
|
27
|
+
|
|
28
|
+
- name: Initialize CodeQL
|
|
29
|
+
uses: github/codeql-action/init@v4
|
|
30
|
+
with:
|
|
31
|
+
languages: ${{ matrix.language }}
|
|
32
|
+
|
|
33
|
+
- name: Autobuild
|
|
34
|
+
uses: github/codeql-action/autobuild@v4
|
|
35
|
+
|
|
36
|
+
- name: Perform CodeQL Analysis
|
|
37
|
+
uses: github/codeql-action/analyze@v4
|
|
38
|
+
with:
|
|
39
|
+
category: "/language:${{ matrix.language }}"
|