bitranox-template-py-cli 1.5.2__tar.gz → 1.5.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.
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.github/workflows/default_cicd_public.yml +84 -16
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/CHANGELOG.md +16 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/Makefile +1 -1
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/PKG-INFO +26 -16
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/README.md +11 -1
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/pyproject.toml +28 -23
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/__init__conf__.py +1 -1
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/commands/config.py +12 -11
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/commands/email/_common.py +8 -7
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/commands/email/send_email.py +7 -8
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/commands/email/send_notification.py +5 -6
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/commands/logging.py +2 -1
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/root.py +6 -5
- bitranox_template_py_cli-1.5.4/src/bitranox_template_py_cli/adapters/cli/typed_click.py +37 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/email/transport.py +6 -4
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_module_entry.py +2 -2
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.devcontainer/devcontainer.json +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.devcontainer/settings.json +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.env.example +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.github/actions/extract-metadata/action.yml +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.github/dependabot.yml +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.github/workflows/codeql.yml +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.github/workflows/default_release_public.yml +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.gitignore +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.qlty/qlty.toml +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.snyk +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/CONFIG.md +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/CONTRIBUTING.md +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/DEVELOPMENT.md +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/INSTALL.md +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/LICENSE +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/SECURITY.md +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/codecov.yml +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/docs/adr/0001-memory-adapters-in-src.md +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/docs/systemdesign/module_reference.md +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/notebooks/Quickstart.ipynb +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/rename.sh +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/rename_dry.sh +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/reset_git_history.sh +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/__init__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/__main__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/__init__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/__init__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/commands/__init__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/commands/email/__init__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/commands/info.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/constants.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/context.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/exit_codes.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/main.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/cli/py.typed +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/config/__init__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/config/defaultconfig.d/40-layered-config.toml +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/config/defaultconfig.d/50-mail.toml +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/config/defaultconfig.d/90-logging.toml +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/config/defaultconfig.toml +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/config/deploy.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/config/display.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/config/loader.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/config/overrides.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/config/permissions.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/config/py.typed +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/email/__init__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/email/config.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/email/py.typed +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/email/sender.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/email/validation.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/logging/__init__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/logging/py.typed +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/logging/setup.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/memory/__init__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/memory/config.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/memory/email.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/memory/logging.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/adapters/py.typed +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/application/__init__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/application/ports.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/application/py.typed +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/composition/__init__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/composition/py.typed +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/domain/__init__.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/domain/behaviors.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/domain/enums.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/domain/errors.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/domain/py.typed +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/entry.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/src/bitranox_template_py_cli/py.typed +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/conftest.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_behaviors.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cache_effectiveness.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cli_config.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cli_core.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cli_email.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cli_env_file.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cli_exit_codes.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cli_overrides.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cli_validation.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_config_overrides.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_deploy_permissions.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_display.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_enums.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_errors.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_logging.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_mail.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_metadata.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_metadata_sync.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_ports.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_property_email.py +0 -0
- {bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_property_overrides.py +0 -0
|
@@ -11,24 +11,27 @@ on:
|
|
|
11
11
|
|
|
12
12
|
jobs:
|
|
13
13
|
setup:
|
|
14
|
-
name: Extract Python versions from pyproject.toml
|
|
14
|
+
name: Extract Python versions and OS matrix from pyproject.toml
|
|
15
15
|
runs-on: ubuntu-latest
|
|
16
16
|
outputs:
|
|
17
|
-
python-versions: ${{ steps.
|
|
18
|
-
python-latest: ${{ steps.
|
|
17
|
+
python-versions: ${{ steps.metadata.outputs.python-versions }}
|
|
18
|
+
python-latest: ${{ steps.metadata.outputs.python-latest }}
|
|
19
|
+
os-matrix: ${{ steps.metadata.outputs.os-matrix }}
|
|
19
20
|
steps:
|
|
20
21
|
- uses: actions/checkout@v6
|
|
21
22
|
- uses: actions/setup-python@v6
|
|
22
23
|
with:
|
|
23
24
|
python-version: "3.x"
|
|
24
|
-
- name: Parse
|
|
25
|
-
id:
|
|
25
|
+
- name: Parse pyproject.toml for Python versions and OS matrix
|
|
26
|
+
id: metadata
|
|
26
27
|
shell: python
|
|
27
28
|
run: |
|
|
28
29
|
import json, os, tomllib
|
|
29
30
|
|
|
30
31
|
with open("pyproject.toml", "rb") as f:
|
|
31
32
|
data = tomllib.load(f)
|
|
33
|
+
|
|
34
|
+
# Python versions from classifiers
|
|
32
35
|
classifiers = data.get("project", {}).get("classifiers", [])
|
|
33
36
|
prefix = "Programming Language :: Python :: "
|
|
34
37
|
versions = [
|
|
@@ -38,9 +41,15 @@ jobs:
|
|
|
38
41
|
]
|
|
39
42
|
if not versions:
|
|
40
43
|
raise SystemExit("No Python X.Y versions found in classifiers")
|
|
44
|
+
|
|
45
|
+
# OS matrix from [tool.ci]
|
|
46
|
+
ci_config = data.get("tool", {}).get("ci", {})
|
|
47
|
+
os_list = ci_config.get("os", ["ubuntu-latest"])
|
|
48
|
+
|
|
41
49
|
with open(os.environ["GITHUB_OUTPUT"], "a") as fh:
|
|
42
50
|
fh.write(f"python-versions={json.dumps(versions)}\n")
|
|
43
51
|
fh.write(f"python-latest={versions[-1]}\n")
|
|
52
|
+
fh.write(f"os-matrix={json.dumps(os_list)}\n")
|
|
44
53
|
|
|
45
54
|
test:
|
|
46
55
|
name: Tests (Python ${{ matrix.python }}, ${{ matrix.os }})
|
|
@@ -49,7 +58,7 @@ jobs:
|
|
|
49
58
|
strategy:
|
|
50
59
|
fail-fast: false
|
|
51
60
|
matrix:
|
|
52
|
-
os:
|
|
61
|
+
os: ${{ fromJSON(needs.setup.outputs.os-matrix) }}
|
|
53
62
|
python: ${{ fromJSON(needs.setup.outputs.python-versions) }}
|
|
54
63
|
env:
|
|
55
64
|
PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1"
|
|
@@ -77,23 +86,83 @@ jobs:
|
|
|
77
86
|
id: metadata
|
|
78
87
|
uses: ./.github/actions/extract-metadata
|
|
79
88
|
|
|
80
|
-
- name: Install
|
|
89
|
+
- name: Install CI system dependencies (Ubuntu)
|
|
81
90
|
if: runner.os == 'Linux'
|
|
82
|
-
shell:
|
|
91
|
+
shell: python
|
|
83
92
|
run: |
|
|
84
|
-
|
|
85
|
-
|
|
93
|
+
import subprocess
|
|
94
|
+
try:
|
|
95
|
+
import tomllib
|
|
96
|
+
except ModuleNotFoundError:
|
|
97
|
+
import pip._vendor.tomli as tomllib
|
|
98
|
+
with open("pyproject.toml", "rb") as f:
|
|
99
|
+
data = tomllib.load(f)
|
|
100
|
+
deps = data.get("tool", {}).get("ci", {}).get("system-dependencies-ubuntu", [])
|
|
101
|
+
if deps:
|
|
102
|
+
print(f"Installing system packages: {' '.join(deps)}")
|
|
103
|
+
subprocess.check_call(["sudo", "apt-get", "update"])
|
|
104
|
+
subprocess.check_call(["sudo", "apt-get", "install", "-y", *deps])
|
|
105
|
+
else:
|
|
106
|
+
print("No CI system dependencies configured for Ubuntu.")
|
|
107
|
+
|
|
108
|
+
- name: Install CI system dependencies (macOS)
|
|
109
|
+
if: runner.os == 'macOS'
|
|
110
|
+
shell: python
|
|
111
|
+
run: |
|
|
112
|
+
import subprocess
|
|
113
|
+
try:
|
|
114
|
+
import tomllib
|
|
115
|
+
except ModuleNotFoundError:
|
|
116
|
+
import pip._vendor.tomli as tomllib
|
|
117
|
+
with open("pyproject.toml", "rb") as f:
|
|
118
|
+
data = tomllib.load(f)
|
|
119
|
+
deps = data.get("tool", {}).get("ci", {}).get("system-dependencies-macos", [])
|
|
120
|
+
if deps:
|
|
121
|
+
print(f"Installing system packages: {' '.join(deps)}")
|
|
122
|
+
subprocess.check_call(["brew", "install", *deps])
|
|
123
|
+
else:
|
|
124
|
+
print("No CI system dependencies configured for macOS.")
|
|
86
125
|
|
|
87
126
|
- name: Install dev deps
|
|
88
127
|
shell: bash
|
|
89
128
|
run: |
|
|
90
129
|
uv pip install -e .[dev] --system
|
|
91
130
|
|
|
92
|
-
- name: Install
|
|
131
|
+
- name: Install CI system dependencies (Windows/choco)
|
|
93
132
|
if: runner.os == 'Windows'
|
|
94
|
-
shell:
|
|
133
|
+
shell: python
|
|
95
134
|
run: |
|
|
96
|
-
|
|
135
|
+
import subprocess
|
|
136
|
+
try:
|
|
137
|
+
import tomllib
|
|
138
|
+
except ModuleNotFoundError:
|
|
139
|
+
import pip._vendor.tomli as tomllib
|
|
140
|
+
with open("pyproject.toml", "rb") as f:
|
|
141
|
+
data = tomllib.load(f)
|
|
142
|
+
deps = data.get("tool", {}).get("ci", {}).get("system-dependencies-windows-choco", [])
|
|
143
|
+
if deps:
|
|
144
|
+
print(f"Installing choco packages: {' '.join(deps)}")
|
|
145
|
+
subprocess.check_call(["choco", "install", "-y", *deps])
|
|
146
|
+
else:
|
|
147
|
+
print("No CI choco dependencies configured for Windows.")
|
|
148
|
+
|
|
149
|
+
- name: Install CI pip dependencies (Windows)
|
|
150
|
+
if: runner.os == 'Windows'
|
|
151
|
+
shell: python
|
|
152
|
+
run: |
|
|
153
|
+
import subprocess
|
|
154
|
+
try:
|
|
155
|
+
import tomllib
|
|
156
|
+
except ModuleNotFoundError:
|
|
157
|
+
import pip._vendor.tomli as tomllib
|
|
158
|
+
with open("pyproject.toml", "rb") as f:
|
|
159
|
+
data = tomllib.load(f)
|
|
160
|
+
deps = data.get("tool", {}).get("ci", {}).get("system-dependencies-windows-pip", [])
|
|
161
|
+
if deps:
|
|
162
|
+
print(f"Installing pip packages: {' '.join(deps)}")
|
|
163
|
+
subprocess.check_call(["uv", "pip", "install", *deps, "--system"])
|
|
164
|
+
else:
|
|
165
|
+
print("No CI pip dependencies configured for Windows.")
|
|
97
166
|
|
|
98
167
|
# Self-hosted runners persist ruff cache via RUFF_CACHE_DIR volume mount,
|
|
99
168
|
# so actions/cache is only needed on GitHub-hosted runners.
|
|
@@ -145,7 +214,7 @@ jobs:
|
|
|
145
214
|
&& steps.pytest.outcome == 'success'
|
|
146
215
|
&& matrix.os == 'ubuntu-latest'
|
|
147
216
|
&& matrix.python == needs.setup.outputs.python-latest
|
|
148
|
-
uses: codecov/codecov-action@
|
|
217
|
+
uses: codecov/codecov-action@v7
|
|
149
218
|
with:
|
|
150
219
|
files: ${{ env.COV_REPORT_FILE }}
|
|
151
220
|
fail_ci_if_error: false
|
|
@@ -201,8 +270,7 @@ jobs:
|
|
|
201
270
|
exit 1
|
|
202
271
|
fi
|
|
203
272
|
if [[ "${{ steps.pipaudit.outcome }}" != "success" ]]; then
|
|
204
|
-
echo "::
|
|
205
|
-
exit 1
|
|
273
|
+
echo "::warning::pip-audit vulnerability scan failed"
|
|
206
274
|
fi
|
|
207
275
|
echo "All checks passed!"
|
|
208
276
|
|
|
@@ -6,6 +6,22 @@ the [Keep a Changelog](https://keepachangelog.com/) format.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.5.4] 2026-06-14
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Added a `typed_click.py` facade wrapping rich-click's `option` / `version_option` decorators behind explicit, fully-known signatures, keeping the CLI strict-clean under pyright 1.1.410 (`reportUnknownMemberType`) without disabling the rule (ignore isolated to the facade).
|
|
13
|
+
- Bumped internal dependency floors: `lib_cli_exit_tools>=2.3.2`, `lib_log_rich>=6.3.5`, `lib_layered_config>=5.5.2`, `btx_lib_mail>=1.3.2`.
|
|
14
|
+
|
|
15
|
+
## [1.5.3] - 2026-03-30
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- Bumped Codecov GitHub Action to V6
|
|
19
|
+
- Updated CVE exclusion list: removed stale entries, added inline documentation for remaining exclusions
|
|
20
|
+
- pip-audit set to warning-only to reduce CI noise
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- Email transport: minor improvements to SMTP handling
|
|
24
|
+
|
|
9
25
|
## [1.5.2] - 2026-03-05
|
|
10
26
|
|
|
11
27
|
### Fixed
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# BMK MAKEFILE 2.9.
|
|
1
|
+
# BMK MAKEFILE 2.9.4
|
|
2
2
|
# do not alter this file - it might be overwritten on new versions of BMK
|
|
3
3
|
# if You want to alter it, remove the first line # BMK MAKEFILE 1.0 - then it is a custom makefile and will not be overwritten
|
|
4
4
|
# bmk Makefile — thin wrapper using `uv tool install` for persistent bmk
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bitranox_template_py_cli
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.4
|
|
4
4
|
Summary: Template CLI application with configuration management and structured logging
|
|
5
5
|
Project-URL: Homepage, https://github.com/bitranox/bitranox_template_py_cli
|
|
6
6
|
Project-URL: Repository, https://github.com/bitranox/bitranox_template_py_cli.git
|
|
@@ -21,33 +21,33 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.14
|
|
22
22
|
Classifier: Typing :: Typed
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
|
-
Requires-Dist: btx-lib-mail>=1.3.
|
|
25
|
-
Requires-Dist: lib-cli-exit-tools>=2.3.
|
|
26
|
-
Requires-Dist: lib-layered-config>=5.5.
|
|
27
|
-
Requires-Dist: lib-log-rich>=6.3.
|
|
24
|
+
Requires-Dist: btx-lib-mail>=1.3.2
|
|
25
|
+
Requires-Dist: lib-cli-exit-tools>=2.3.2
|
|
26
|
+
Requires-Dist: lib-layered-config>=5.5.2
|
|
27
|
+
Requires-Dist: lib-log-rich>=6.3.5
|
|
28
28
|
Requires-Dist: orjson>=3.11.7
|
|
29
29
|
Requires-Dist: pydantic>=2.12.5
|
|
30
30
|
Requires-Dist: rich-click>=1.9.7
|
|
31
31
|
Provides-Extra: dev
|
|
32
32
|
Requires-Dist: bandit>=1.9.4; extra == 'dev'
|
|
33
|
-
Requires-Dist: build>=1.4.
|
|
34
|
-
Requires-Dist: codecov-cli>=11.2.
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist: hypothesis>=6.151.
|
|
37
|
-
Requires-Dist: import-linter>=2.
|
|
38
|
-
Requires-Dist: jaraco-context>=6.1.
|
|
33
|
+
Requires-Dist: build>=1.4.2; extra == 'dev'
|
|
34
|
+
Requires-Dist: codecov-cli>=11.2.8; extra == 'dev'
|
|
35
|
+
Requires-Dist: httpx2>=2.2.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: hypothesis>=6.151.10; extra == 'dev'
|
|
37
|
+
Requires-Dist: import-linter>=2.11; extra == 'dev'
|
|
38
|
+
Requires-Dist: jaraco-context>=6.1.2; extra == 'dev'
|
|
39
39
|
Requires-Dist: pip-audit>=2.10.0; extra == 'dev'
|
|
40
40
|
Requires-Dist: pynacl>=1.6.2; extra == 'dev'
|
|
41
41
|
Requires-Dist: pyright[nodejs]>=1.1.408; extra == 'dev'
|
|
42
|
-
Requires-Dist: pytest-cov>=7.
|
|
42
|
+
Requires-Dist: pytest-cov>=7.1.0; extra == 'dev'
|
|
43
43
|
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
44
44
|
Requires-Dist: python-multipart>=0.0.22; extra == 'dev'
|
|
45
45
|
Requires-Dist: rtoml>=0.13.0; extra == 'dev'
|
|
46
|
-
Requires-Dist: ruff>=0.15.
|
|
47
|
-
Requires-Dist: textual>=8.
|
|
46
|
+
Requires-Dist: ruff>=0.15.8; extra == 'dev'
|
|
47
|
+
Requires-Dist: textual>=8.2.1; extra == 'dev'
|
|
48
48
|
Requires-Dist: twine>=6.2.0; extra == 'dev'
|
|
49
49
|
Requires-Dist: urllib3>=2.6.3; extra == 'dev'
|
|
50
|
-
Requires-Dist: virtualenv>=21.
|
|
50
|
+
Requires-Dist: virtualenv>=21.2.0; extra == 'dev'
|
|
51
51
|
Requires-Dist: wheel>=0.46.3; extra == 'dev'
|
|
52
52
|
Description-Content-Type: text/markdown
|
|
53
53
|
|
|
@@ -63,7 +63,6 @@ Description-Content-Type: text/markdown
|
|
|
63
63
|
[](https://docs.astral.sh/ruff/)
|
|
64
64
|
[](https://codecov.io/gh/bitranox/bitranox_template_py_cli)
|
|
65
65
|
[](https://qlty.sh/gh/bitranox/projects/bitranox_template_py_cli)
|
|
66
|
-
[](https://snyk.io/test/github/bitranox/bitranox_template_py_cli)
|
|
67
66
|
[](https://github.com/PyCQA/bandit)
|
|
68
67
|
|
|
69
68
|
|
|
@@ -95,6 +94,14 @@ Description-Content-Type: text/markdown
|
|
|
95
94
|
```bash
|
|
96
95
|
# macOS/Linux
|
|
97
96
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
97
|
+
|
|
98
|
+
# Copy the actual binaries
|
|
99
|
+
cp /root/.local/bin/uv /usr/local/bin/uv
|
|
100
|
+
cp /root/.local/bin/uvx /usr/local/bin/uvx
|
|
101
|
+
|
|
102
|
+
# Ensure world-executable
|
|
103
|
+
chmod 755 /usr/local/bin/uv /usr/local/bin/uvx
|
|
104
|
+
|
|
98
105
|
# Windows (PowerShell)
|
|
99
106
|
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
100
107
|
```
|
|
@@ -159,6 +166,9 @@ uv tool install bitranox_template_py_cli
|
|
|
159
166
|
# Verify
|
|
160
167
|
bitranox-template-py-cli --version
|
|
161
168
|
|
|
169
|
+
# deploy config files
|
|
170
|
+
bitranox-template-py-cli deploy-config --target app
|
|
171
|
+
|
|
162
172
|
# Try it out
|
|
163
173
|
bitranox-template-py-cli hello
|
|
164
174
|
bitranox-template-py-cli info
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
[](https://docs.astral.sh/ruff/)
|
|
11
11
|
[](https://codecov.io/gh/bitranox/bitranox_template_py_cli)
|
|
12
12
|
[](https://qlty.sh/gh/bitranox/projects/bitranox_template_py_cli)
|
|
13
|
-
[](https://snyk.io/test/github/bitranox/bitranox_template_py_cli)
|
|
14
13
|
[](https://github.com/PyCQA/bandit)
|
|
15
14
|
|
|
16
15
|
|
|
@@ -42,6 +41,14 @@
|
|
|
42
41
|
```bash
|
|
43
42
|
# macOS/Linux
|
|
44
43
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
44
|
+
|
|
45
|
+
# Copy the actual binaries
|
|
46
|
+
cp /root/.local/bin/uv /usr/local/bin/uv
|
|
47
|
+
cp /root/.local/bin/uvx /usr/local/bin/uvx
|
|
48
|
+
|
|
49
|
+
# Ensure world-executable
|
|
50
|
+
chmod 755 /usr/local/bin/uv /usr/local/bin/uvx
|
|
51
|
+
|
|
45
52
|
# Windows (PowerShell)
|
|
46
53
|
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
47
54
|
```
|
|
@@ -106,6 +113,9 @@ uv tool install bitranox_template_py_cli
|
|
|
106
113
|
# Verify
|
|
107
114
|
bitranox-template-py-cli --version
|
|
108
115
|
|
|
116
|
+
# deploy config files
|
|
117
|
+
bitranox-template-py-cli deploy-config --target app
|
|
118
|
+
|
|
109
119
|
# Try it out
|
|
110
120
|
bitranox-template-py-cli hello
|
|
111
121
|
bitranox-template-py-cli info
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "bitranox_template_py_cli"
|
|
3
|
-
version = "1.5.
|
|
3
|
+
version = "1.5.4"
|
|
4
4
|
description = "Template CLI application with configuration management and structured logging"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
7
|
dependencies = [
|
|
8
8
|
"rich-click>=1.9.7",
|
|
9
|
-
"lib_cli_exit_tools>=2.3.
|
|
10
|
-
"lib_log_rich>=6.3.
|
|
11
|
-
"lib_layered_config>=5.5.
|
|
12
|
-
"btx_lib_mail>=1.3.
|
|
9
|
+
"lib_cli_exit_tools>=2.3.2",
|
|
10
|
+
"lib_log_rich>=6.3.5",
|
|
11
|
+
"lib_layered_config>=5.5.2",
|
|
12
|
+
"btx_lib_mail>=1.3.2",
|
|
13
13
|
"pydantic>=2.12.5",
|
|
14
14
|
"orjson>=3.11.7",
|
|
15
15
|
]
|
|
@@ -30,6 +30,15 @@ classifiers = [
|
|
|
30
30
|
"Typing :: Typed",
|
|
31
31
|
]
|
|
32
32
|
|
|
33
|
+
# System packages required for CI runners (read by GitHub Actions workflow template)
|
|
34
|
+
# Format: "apt_package_name" for Ubuntu, future: {"apt": "...", "brew": "...", "choco": "..."}
|
|
35
|
+
[tool.ci]
|
|
36
|
+
system-dependencies-ubuntu = []
|
|
37
|
+
system-dependencies-macos = []
|
|
38
|
+
system-dependencies-windows-pip = []
|
|
39
|
+
system-dependencies-windows-choco = []
|
|
40
|
+
os = ["ubuntu-latest", "windows-latest", "macos-latest"]
|
|
41
|
+
|
|
33
42
|
[project.urls]
|
|
34
43
|
Homepage = "https://github.com/bitranox/bitranox_template_py_cli"
|
|
35
44
|
Repository = "https://github.com/bitranox/bitranox_template_py_cli.git"
|
|
@@ -38,26 +47,26 @@ Issues = "https://github.com/bitranox/bitranox_template_py_cli/issues"
|
|
|
38
47
|
[project.optional-dependencies]
|
|
39
48
|
dev = [
|
|
40
49
|
"pytest>=9.0.2",
|
|
41
|
-
"pytest-cov>=7.
|
|
42
|
-
"hypothesis>=6.151.
|
|
43
|
-
"ruff>=0.15.
|
|
50
|
+
"pytest-cov>=7.1.0",
|
|
51
|
+
"hypothesis>=6.151.10",
|
|
52
|
+
"ruff>=0.15.8",
|
|
44
53
|
"pyright[nodejs]>=1.1.408",
|
|
45
54
|
"bandit>=1.9.4",
|
|
46
|
-
"build>=1.4.
|
|
55
|
+
"build>=1.4.2",
|
|
47
56
|
"twine>=6.2.0",
|
|
48
|
-
"codecov-cli>=11.2.
|
|
57
|
+
"codecov-cli>=11.2.8",
|
|
49
58
|
"pip-audit>=2.10.0",
|
|
50
|
-
"textual>=8.
|
|
51
|
-
"import-linter>=2.
|
|
59
|
+
"textual>=8.2.1",
|
|
60
|
+
"import-linter>=2.11",
|
|
52
61
|
"rtoml>=0.13.0",
|
|
53
|
-
"
|
|
62
|
+
"httpx2>=2.2.0",
|
|
54
63
|
# Transitive CVE pins
|
|
55
|
-
"jaraco-context>=6.1.
|
|
64
|
+
"jaraco-context>=6.1.2", # CVE-2025-23949
|
|
56
65
|
"python-multipart>=0.0.22", # CVE-2025-24486
|
|
57
66
|
"wheel>=0.46.3", # CVE-2025-24049
|
|
58
67
|
"pynacl>=1.6.2", # CVE-2025-69277
|
|
59
68
|
"urllib3>=2.6.3", # CVE-2025-66471, CVE-2025-66418
|
|
60
|
-
"virtualenv>=21.
|
|
69
|
+
"virtualenv>=21.2.0", # CVE-2025-22702
|
|
61
70
|
]
|
|
62
71
|
|
|
63
72
|
[project.scripts]
|
|
@@ -248,14 +257,10 @@ layers = [
|
|
|
248
257
|
# PYSEC-2022-42969: py library ReDoS — py 1.11.0 is installed in the development
|
|
249
258
|
# environment but is not a dependency of this project; no fix available.
|
|
250
259
|
ignore-vulns = [
|
|
251
|
-
"PYSEC-2022-42969",
|
|
252
|
-
"
|
|
253
|
-
"
|
|
254
|
-
"CVE-
|
|
255
|
-
"CVE-2025-8869", # pip 24.3.1 — env-only, not a project dependency
|
|
256
|
-
"CVE-2026-1703",
|
|
257
|
-
"CVE-2026-25990",
|
|
258
|
-
"CVE-2026-26007",
|
|
260
|
+
"PYSEC-2022-42969", # py 1.11.0 — ReDoS, env-only, no fix available
|
|
261
|
+
"CVE-2025-8869", # pip 24.3.1 — env-only, not a project dependency
|
|
262
|
+
"CVE-2026-1703", # pip 24.3.1 — env-only, not a project dependency
|
|
263
|
+
"CVE-2026-25990", # pillow 12.0.0 — env-only, fix in 12.1.1
|
|
259
264
|
]
|
|
260
265
|
|
|
261
266
|
[tool.bashate]
|
|
@@ -40,7 +40,7 @@ name = "bitranox_template_py_cli"
|
|
|
40
40
|
#: Human-readable summary shown in CLI help output.
|
|
41
41
|
title = "Template CLI application with configuration management and structured logging"
|
|
42
42
|
#: Current release version pulled from ``pyproject.toml`` by automation.
|
|
43
|
-
version = "1.5.
|
|
43
|
+
version = "1.5.4"
|
|
44
44
|
#: Repository homepage presented to users.
|
|
45
45
|
homepage = "https://github.com/bitranox/bitranox_template_py_cli"
|
|
46
46
|
#: Author attribution surfaced in CLI output.
|
|
@@ -25,25 +25,26 @@ from bitranox_template_py_cli.domain.enums import DeployTarget, OutputFormat
|
|
|
25
25
|
from ..constants import CLICK_CONTEXT_SETTINGS
|
|
26
26
|
from ..context import CLIContext, get_cli_context
|
|
27
27
|
from ..exit_codes import ExitCode
|
|
28
|
+
from ..typed_click import option
|
|
28
29
|
|
|
29
30
|
logger = logging.getLogger(__name__)
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
@click.command("config", context_settings=CLICK_CONTEXT_SETTINGS)
|
|
33
|
-
@
|
|
34
|
+
@option(
|
|
34
35
|
"--format",
|
|
35
36
|
"output_format",
|
|
36
37
|
type=click.Choice([f.value for f in OutputFormat], case_sensitive=False),
|
|
37
38
|
default=OutputFormat.HUMAN.value,
|
|
38
39
|
help="Output format (human-readable or JSON)",
|
|
39
40
|
)
|
|
40
|
-
@
|
|
41
|
+
@option(
|
|
41
42
|
"--section",
|
|
42
43
|
type=str,
|
|
43
44
|
default=None,
|
|
44
45
|
help="Show only a specific configuration section (e.g., 'lib_log_rich')",
|
|
45
46
|
)
|
|
46
|
-
@
|
|
47
|
+
@option(
|
|
47
48
|
"--profile",
|
|
48
49
|
type=str,
|
|
49
50
|
default=None,
|
|
@@ -134,7 +135,7 @@ def _parse_octal_mode(ctx: click.Context, param: click.Parameter, value: str | N
|
|
|
134
135
|
|
|
135
136
|
|
|
136
137
|
@click.command("config-deploy", context_settings=CLICK_CONTEXT_SETTINGS)
|
|
137
|
-
@
|
|
138
|
+
@option(
|
|
138
139
|
"--target",
|
|
139
140
|
"targets",
|
|
140
141
|
type=click.Choice([t.value for t in DeployTarget], case_sensitive=False),
|
|
@@ -142,32 +143,32 @@ def _parse_octal_mode(ctx: click.Context, param: click.Parameter, value: str | N
|
|
|
142
143
|
required=True,
|
|
143
144
|
help="Target configuration layer(s) to deploy to (can specify multiple)",
|
|
144
145
|
)
|
|
145
|
-
@
|
|
146
|
+
@option(
|
|
146
147
|
"--force",
|
|
147
148
|
is_flag=True,
|
|
148
149
|
default=False,
|
|
149
150
|
help="Overwrite existing configuration files",
|
|
150
151
|
)
|
|
151
|
-
@
|
|
152
|
+
@option(
|
|
152
153
|
"--profile",
|
|
153
154
|
type=str,
|
|
154
155
|
default=None,
|
|
155
156
|
help="Override profile from root command (e.g., 'production', 'test')",
|
|
156
157
|
)
|
|
157
|
-
@
|
|
158
|
+
@option(
|
|
158
159
|
"--permissions/--no-permissions",
|
|
159
160
|
"set_permissions",
|
|
160
161
|
default=None,
|
|
161
162
|
help="Set Unix permissions (755/644 for app/host, 700/600 for user). Default: enabled.",
|
|
162
163
|
)
|
|
163
|
-
@
|
|
164
|
+
@option(
|
|
164
165
|
"--dir-mode",
|
|
165
166
|
type=str,
|
|
166
167
|
default=None,
|
|
167
168
|
callback=_parse_octal_mode,
|
|
168
169
|
help="Override directory mode (octal, e.g., 750 or 0o750)",
|
|
169
170
|
)
|
|
170
|
-
@
|
|
171
|
+
@option(
|
|
171
172
|
"--file-mode",
|
|
172
173
|
type=str,
|
|
173
174
|
default=None,
|
|
@@ -290,8 +291,8 @@ def _report_deployment_result(deployed_paths: list[Path], profile: str | None, s
|
|
|
290
291
|
|
|
291
292
|
|
|
292
293
|
@click.command("config-generate-examples", context_settings=CLICK_CONTEXT_SETTINGS)
|
|
293
|
-
@
|
|
294
|
-
@
|
|
294
|
+
@option("--destination", type=click.Path(file_okay=False), required=True, help="Directory to write example files")
|
|
295
|
+
@option("--force", is_flag=True, default=False, help="Overwrite existing files")
|
|
295
296
|
@click.pass_context
|
|
296
297
|
def cli_config_generate_examples(ctx: click.Context, destination: str, force: bool) -> None:
|
|
297
298
|
"""Generate example configuration files in a target directory.
|
|
@@ -22,6 +22,7 @@ from bitranox_template_py_cli.application.ports import LoadEmailConfigFromDict
|
|
|
22
22
|
from bitranox_template_py_cli.domain.errors import ConfigurationError, DeliveryError
|
|
23
23
|
|
|
24
24
|
from ...exit_codes import ExitCode
|
|
25
|
+
from ...typed_click import option
|
|
25
26
|
|
|
26
27
|
logger = logging.getLogger(__name__)
|
|
27
28
|
|
|
@@ -80,23 +81,23 @@ def smtp_config_options(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
|
80
81
|
can be overridden at invocation time.
|
|
81
82
|
"""
|
|
82
83
|
options = [
|
|
83
|
-
|
|
84
|
+
option(
|
|
84
85
|
"--smtp-host",
|
|
85
86
|
"smtp_hosts",
|
|
86
87
|
multiple=True,
|
|
87
88
|
default=(),
|
|
88
89
|
help="Override SMTP host (can specify multiple; format host:port)",
|
|
89
90
|
),
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
option("--smtp-username", default=None, help="Override SMTP authentication username"),
|
|
92
|
+
option("--smtp-password", default=None, help="Override SMTP authentication password"),
|
|
93
|
+
option("--use-starttls/--no-use-starttls", default=None, help="Override STARTTLS setting"),
|
|
94
|
+
option("--timeout", "timeout", type=float, default=None, help="Override socket timeout in seconds"),
|
|
95
|
+
option(
|
|
95
96
|
"--raise-on-missing-attachments/--no-raise-on-missing-attachments",
|
|
96
97
|
default=None,
|
|
97
98
|
help="Override missing attachment handling",
|
|
98
99
|
),
|
|
99
|
-
|
|
100
|
+
option(
|
|
100
101
|
"--raise-on-invalid-recipient/--no-raise-on-invalid-recipient",
|
|
101
102
|
default=None,
|
|
102
103
|
help="Override invalid recipient handling",
|
|
@@ -15,6 +15,7 @@ from pydantic import ValidationError
|
|
|
15
15
|
|
|
16
16
|
from ...constants import CLICK_CONTEXT_SETTINGS
|
|
17
17
|
from ...context import get_cli_context
|
|
18
|
+
from ...typed_click import option
|
|
18
19
|
from ._common import (
|
|
19
20
|
apply_validated_overrides,
|
|
20
21
|
execute_with_email_error_handling,
|
|
@@ -53,20 +54,18 @@ def _log_send_email_start(
|
|
|
53
54
|
|
|
54
55
|
|
|
55
56
|
@click.command("send-email", context_settings=CLICK_CONTEXT_SETTINGS)
|
|
56
|
-
@
|
|
57
|
+
@option(
|
|
57
58
|
"--to",
|
|
58
59
|
"recipients",
|
|
59
60
|
multiple=True,
|
|
60
61
|
required=False,
|
|
61
62
|
help="Recipient email address (can specify multiple; uses config default if not specified)",
|
|
62
63
|
)
|
|
63
|
-
@
|
|
64
|
-
@
|
|
65
|
-
@
|
|
66
|
-
@
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
@click.option(
|
|
64
|
+
@option("--subject", required=True, help="Email subject line")
|
|
65
|
+
@option("--body", default="", help="Plain-text email body")
|
|
66
|
+
@option("--body-html", default="", help="HTML email body (sent as multipart with plain text)")
|
|
67
|
+
@option("--from", "from_address", default=None, help="Override sender address (uses config default if not specified)")
|
|
68
|
+
@option(
|
|
70
69
|
"--attachment",
|
|
71
70
|
"attachments",
|
|
72
71
|
multiple=True,
|
|
@@ -14,6 +14,7 @@ from pydantic import ValidationError
|
|
|
14
14
|
|
|
15
15
|
from ...constants import CLICK_CONTEXT_SETTINGS
|
|
16
16
|
from ...context import get_cli_context
|
|
17
|
+
from ...typed_click import option
|
|
17
18
|
from ._common import (
|
|
18
19
|
apply_validated_overrides,
|
|
19
20
|
execute_with_email_error_handling,
|
|
@@ -27,18 +28,16 @@ logger = logging.getLogger(__name__)
|
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
@click.command("send-notification", context_settings=CLICK_CONTEXT_SETTINGS)
|
|
30
|
-
@
|
|
31
|
+
@option(
|
|
31
32
|
"--to",
|
|
32
33
|
"recipients",
|
|
33
34
|
multiple=True,
|
|
34
35
|
required=False,
|
|
35
36
|
help="Recipient email address (can specify multiple; uses config default if not specified)",
|
|
36
37
|
)
|
|
37
|
-
@
|
|
38
|
-
@
|
|
39
|
-
@
|
|
40
|
-
"--from", "from_address", default=None, help="Override sender address (uses config default if not specified)"
|
|
41
|
-
)
|
|
38
|
+
@option("--subject", required=True, help="Notification subject line")
|
|
39
|
+
@option("--message", required=True, help="Notification message (plain text)")
|
|
40
|
+
@option("--from", "from_address", default=None, help="Override sender address (uses config default if not specified)")
|
|
42
41
|
@smtp_config_options
|
|
43
42
|
@click.pass_context
|
|
44
43
|
def cli_send_notification(
|
|
@@ -11,10 +11,11 @@ from __future__ import annotations
|
|
|
11
11
|
import rich_click as click
|
|
12
12
|
|
|
13
13
|
from ..constants import CLICK_CONTEXT_SETTINGS
|
|
14
|
+
from ..typed_click import option
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
@click.command("logdemo", context_settings=CLICK_CONTEXT_SETTINGS)
|
|
17
|
-
@
|
|
18
|
+
@option("--theme", default="classic", help="Logging theme to preview")
|
|
18
19
|
@click.pass_context
|
|
19
20
|
def cli_logdemo(ctx: click.Context, theme: str) -> None:
|
|
20
21
|
"""Run a logging demonstration to preview log output."""
|
|
@@ -19,6 +19,7 @@ from bitranox_template_py_cli.adapters.config.overrides import apply_overrides
|
|
|
19
19
|
|
|
20
20
|
from .constants import CLICK_CONTEXT_SETTINGS
|
|
21
21
|
from .context import apply_traceback_preferences, store_cli_context
|
|
22
|
+
from .typed_click import option, version_option
|
|
22
23
|
|
|
23
24
|
if TYPE_CHECKING:
|
|
24
25
|
from bitranox_template_py_cli.composition import AppServices
|
|
@@ -49,24 +50,24 @@ def _apply_cli_overrides(config: Config, set_overrides: tuple[str, ...]) -> Conf
|
|
|
49
50
|
context_settings=CLICK_CONTEXT_SETTINGS,
|
|
50
51
|
invoke_without_command=True,
|
|
51
52
|
)
|
|
52
|
-
@
|
|
53
|
+
@version_option(
|
|
53
54
|
version=__init__conf__.version,
|
|
54
55
|
prog_name=__init__conf__.shell_command,
|
|
55
56
|
message=f"{__init__conf__.shell_command} version {__init__conf__.version}",
|
|
56
57
|
)
|
|
57
|
-
@
|
|
58
|
+
@option(
|
|
58
59
|
"--traceback/--no-traceback",
|
|
59
60
|
is_flag=True,
|
|
60
61
|
default=False,
|
|
61
62
|
help="Show full Python traceback on errors",
|
|
62
63
|
)
|
|
63
|
-
@
|
|
64
|
+
@option(
|
|
64
65
|
"--profile",
|
|
65
66
|
type=str,
|
|
66
67
|
default=None,
|
|
67
68
|
help="Load configuration from a named profile (e.g., 'production', 'test')",
|
|
68
69
|
)
|
|
69
|
-
@
|
|
70
|
+
@option(
|
|
70
71
|
"--set",
|
|
71
72
|
"set_overrides",
|
|
72
73
|
multiple=True,
|
|
@@ -74,7 +75,7 @@ def _apply_cli_overrides(config: Config, set_overrides: tuple[str, ...]) -> Conf
|
|
|
74
75
|
metavar="SECTION.KEY=VALUE",
|
|
75
76
|
help="Override a configuration setting (repeatable).",
|
|
76
77
|
)
|
|
77
|
-
@
|
|
78
|
+
@option(
|
|
78
79
|
"--env-file",
|
|
79
80
|
"env_file",
|
|
80
81
|
type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Strictly-typed wrappers for the rich_click decorators with unknown types.
|
|
2
|
+
|
|
3
|
+
rich_click ships ``py.typed``, but it re-exports click's ``option`` and
|
|
4
|
+
``version_option`` decorators, whose ``type: ParamType[Unknown]`` parameter makes
|
|
5
|
+
pyright (strict mode) report ``reportUnknownMemberType`` at every call site.
|
|
6
|
+
Wrapping the two affected decorators here behind explicit, fully-known
|
|
7
|
+
signatures keeps the rest of the CLI layer strict-clean without disabling the
|
|
8
|
+
rule. This module is the single boundary that touches the untyped surface, so
|
|
9
|
+
the only ``# pyright: ignore`` for this third-party gap lives here.
|
|
10
|
+
|
|
11
|
+
Other click members (``command``, ``group``, ``echo``, ``Context``, ``Path`` …)
|
|
12
|
+
type cleanly and are still used directly as ``click.X`` at call sites.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from collections.abc import Callable
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
import rich_click as click
|
|
19
|
+
|
|
20
|
+
# Click decorators turn a command function (or another decorator's result) into
|
|
21
|
+
# a wrapped callable. ``Any`` is fully known to pyright (unlike ``Unknown``), so
|
|
22
|
+
# this alias is strict-clean and works both as ``@option(...)`` and as a value
|
|
23
|
+
# collected into a list (see email/_common.py's shared-options pattern).
|
|
24
|
+
_CommandDecorator = Callable[[Callable[..., Any]], Callable[..., Any]]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def option(*param_decls: str, **attrs: Any) -> _CommandDecorator:
|
|
28
|
+
"""Typed wrapper over :func:`rich_click.option`. See module docstring."""
|
|
29
|
+
return click.option(*param_decls, **attrs) # pyright: ignore[reportUnknownMemberType]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def version_option(*param_decls: str, **attrs: Any) -> _CommandDecorator:
|
|
33
|
+
"""Typed wrapper over :func:`rich_click.version_option`. See module docstring."""
|
|
34
|
+
return click.version_option(*param_decls, **attrs) # pyright: ignore[reportUnknownMemberType]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__all__ = ["option", "version_option"]
|
|
@@ -22,13 +22,15 @@ logger = logging.getLogger(__name__)
|
|
|
22
22
|
# Keywords that may indicate sensitive data in exception messages
|
|
23
23
|
_SENSITIVE_KEYWORDS = frozenset(
|
|
24
24
|
{
|
|
25
|
-
"
|
|
26
|
-
"credential",
|
|
25
|
+
"api_key",
|
|
27
26
|
"auth",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
27
|
+
"bearer",
|
|
28
|
+
"credential",
|
|
30
29
|
"key",
|
|
31
30
|
"login",
|
|
31
|
+
"password",
|
|
32
|
+
"secret",
|
|
33
|
+
"token",
|
|
32
34
|
}
|
|
33
35
|
)
|
|
34
36
|
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_module_entry.py
RENAMED
|
@@ -111,7 +111,7 @@ def test_module_entry_subprocess_help() -> None:
|
|
|
111
111
|
This tests the true CLI invocation path that end-users would experience,
|
|
112
112
|
complementing the runpy-based tests that run in-process.
|
|
113
113
|
"""
|
|
114
|
-
result = subprocess.run(
|
|
114
|
+
result = subprocess.run(
|
|
115
115
|
[sys.executable, "-m", "bitranox_template_py_cli", "--help"],
|
|
116
116
|
capture_output=True,
|
|
117
117
|
timeout=30,
|
|
@@ -128,7 +128,7 @@ def test_module_entry_subprocess_help() -> None:
|
|
|
128
128
|
@pytest.mark.os_agnostic
|
|
129
129
|
def test_module_entry_subprocess_version() -> None:
|
|
130
130
|
"""Verify `python -m bitranox_template_py_cli --version` outputs version."""
|
|
131
|
-
result = subprocess.run(
|
|
131
|
+
result = subprocess.run(
|
|
132
132
|
[sys.executable, "-m", "bitranox_template_py_cli", "--version"],
|
|
133
133
|
capture_output=True,
|
|
134
134
|
timeout=30,
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.devcontainer/devcontainer.json
RENAMED
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.devcontainer/settings.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/.github/workflows/codeql.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/notebooks/Quickstart.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cache_effectiveness.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cli_env_file.py
RENAMED
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cli_exit_codes.py
RENAMED
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cli_overrides.py
RENAMED
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_cli_validation.py
RENAMED
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_config_overrides.py
RENAMED
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_deploy_permissions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_metadata_sync.py
RENAMED
|
File without changes
|
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_property_email.py
RENAMED
|
File without changes
|
{bitranox_template_py_cli-1.5.2 → bitranox_template_py_cli-1.5.4}/tests/test_property_overrides.py
RENAMED
|
File without changes
|