lib-detect-testenv 3.0.0__tar.gz → 3.0.2__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.
Files changed (66) hide show
  1. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.devcontainer/devcontainer.json +0 -0
  2. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.devcontainer/settings.json +0 -0
  3. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.env.example +0 -0
  4. lib_detect_testenv-3.0.2/.github/actions/extract-metadata/action.yml +92 -0
  5. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.github/dependabot.yml +0 -0
  6. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.github/workflows/codeql.yml +1 -1
  7. lib_detect_testenv-3.0.2/.github/workflows/default_cicd_public.yml +386 -0
  8. lib_detect_testenv-3.0.0/.github/workflows/release.yml → lib_detect_testenv-3.0.2/.github/workflows/default_release_public.yml +40 -21
  9. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.gitignore +0 -0
  10. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.qlty/qlty.toml +0 -0
  11. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.snyk +0 -0
  12. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/CHANGELOG.md +21 -0
  13. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/CLAUDE.md +47 -14
  14. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/CONTRIBUTING.md +0 -0
  15. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/DEVELOPMENT.md +0 -0
  16. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/INSTALL.md +0 -0
  17. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/LICENSE +0 -0
  18. lib_detect_testenv-3.0.2/Makefile +260 -0
  19. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/PKG-INFO +19 -16
  20. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/README.md +1 -1
  21. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/codecov.yml +0 -0
  22. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/concept.md +0 -0
  23. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/docs/systemdesign/module_reference.md +0 -0
  24. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/notebooks/Quickstart.ipynb +0 -0
  25. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/pyproject.toml +63 -20
  26. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/reset_git_history.sh +16 -15
  27. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/__init__.py +0 -0
  28. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/__init__conf__.py +8 -1
  29. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/__main__.py +0 -0
  30. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/behaviors.py +0 -0
  31. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/cli.py +12 -11
  32. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/lib_detect_testenv.py +0 -0
  33. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/py.typed +0 -0
  34. lib_detect_testenv-3.0.2/src/lib_detect_testenv/typed_click.py +37 -0
  35. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/tests/conftest.py +0 -0
  36. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/tests/test_behaviors.py +0 -0
  37. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/tests/test_cli.py +0 -0
  38. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/tests/test_lib_detect_testenv.py +0 -0
  39. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/tests/test_metadata.py +0 -0
  40. {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/tests/test_module_entry.py +0 -0
  41. lib_detect_testenv-3.0.0/.github/workflows/ci.yml +0 -208
  42. lib_detect_testenv-3.0.0/AGENTS.md +0 -104
  43. lib_detect_testenv-3.0.0/Makefile +0 -54
  44. lib_detect_testenv-3.0.0/README.rst.bak +0 -480
  45. lib_detect_testenv-3.0.0/scripts/__init__.py +0 -3
  46. lib_detect_testenv-3.0.0/scripts/__main__.py +0 -7
  47. lib_detect_testenv-3.0.0/scripts/_utils.py +0 -591
  48. lib_detect_testenv-3.0.0/scripts/build.py +0 -52
  49. lib_detect_testenv-3.0.0/scripts/bump.py +0 -32
  50. lib_detect_testenv-3.0.0/scripts/bump_major.py +0 -20
  51. lib_detect_testenv-3.0.0/scripts/bump_minor.py +0 -20
  52. lib_detect_testenv-3.0.0/scripts/bump_patch.py +0 -20
  53. lib_detect_testenv-3.0.0/scripts/bump_version.py +0 -80
  54. lib_detect_testenv-3.0.0/scripts/clean.py +0 -48
  55. lib_detect_testenv-3.0.0/scripts/cli.py +0 -213
  56. lib_detect_testenv-3.0.0/scripts/dev.py +0 -19
  57. lib_detect_testenv-3.0.0/scripts/help.py +0 -41
  58. lib_detect_testenv-3.0.0/scripts/install.py +0 -19
  59. lib_detect_testenv-3.0.0/scripts/menu.py +0 -568
  60. lib_detect_testenv-3.0.0/scripts/push.py +0 -77
  61. lib_detect_testenv-3.0.0/scripts/release.py +0 -90
  62. lib_detect_testenv-3.0.0/scripts/run_cli.py +0 -154
  63. lib_detect_testenv-3.0.0/scripts/target_metadata.py +0 -174
  64. lib_detect_testenv-3.0.0/scripts/test.py +0 -545
  65. lib_detect_testenv-3.0.0/scripts/version_current.py +0 -20
  66. lib_detect_testenv-3.0.0/tests/test_scripts.py +0 -266
@@ -0,0 +1,92 @@
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
+
33
+ runs:
34
+ using: composite
35
+ steps:
36
+ - name: Extract project metadata from pyproject.toml
37
+ id: meta
38
+ shell: python
39
+ run: |
40
+ import os, subprocess, sys
41
+ try:
42
+ import rtoml
43
+ except ModuleNotFoundError:
44
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "rtoml", "-q"])
45
+ import rtoml
46
+
47
+ from pathlib import Path
48
+ data = rtoml.load(Path('pyproject.toml'))
49
+
50
+ # --- [project] section ---
51
+ project = data['project']['name']
52
+ module = project.replace('-', '_')
53
+ dash = project.replace('_', '-')
54
+ scripts = list(data['project'].get('scripts', {}).keys())
55
+ cli_bin = scripts[0] if scripts else dash
56
+
57
+ # --- [tool.scripts.test] section ---
58
+ scripts_test = data.get('tool', {}).get('scripts', {}).get('test', {})
59
+ src_path = scripts_test.get('src-path', 'src')
60
+ pytest_verbosity = scripts_test.get('pytest-verbosity', '-vv')
61
+ cov_report_file = scripts_test.get('coverage-report-file', 'coverage.xml')
62
+
63
+ # --- [tool.coverage.report] section ---
64
+ cov_fail_under = (
65
+ data.get('tool', {}).get('coverage', {}).get('report', {}).get('fail_under', 90)
66
+ )
67
+
68
+ # --- [tool.bandit] section ---
69
+ bandit_skips = data.get('tool', {}).get('bandit', {}).get('skips', [])
70
+ bandit_skip_flag = f"-s {','.join(bandit_skips)}" if bandit_skips else ""
71
+
72
+ # Write to GITHUB_OUTPUT for composite action outputs
73
+ with open(os.environ['GITHUB_OUTPUT'], 'a', encoding='utf-8') as out:
74
+ out.write(f"PROJECT_NAME={project}\n")
75
+ out.write(f"PACKAGE_MODULE={module}\n")
76
+ out.write(f"CLI_BIN={cli_bin}\n")
77
+ out.write(f"SRC_PATH={src_path}\n")
78
+ out.write(f"PYTEST_VERBOSITY={pytest_verbosity}\n")
79
+ out.write(f"COV_REPORT_FILE={cov_report_file}\n")
80
+ out.write(f"COV_FAIL_UNDER={cov_fail_under}\n")
81
+ out.write(f"BANDIT_SKIP_FLAG={bandit_skip_flag}\n")
82
+
83
+ # Also write to GITHUB_ENV so subsequent steps can use ${{ env.VAR }}
84
+ with open(os.environ['GITHUB_ENV'], 'a', encoding='utf-8') as env:
85
+ env.write(f"PROJECT_NAME={project}\n")
86
+ env.write(f"PACKAGE_MODULE={module}\n")
87
+ env.write(f"CLI_BIN={cli_bin}\n")
88
+ env.write(f"SRC_PATH={src_path}\n")
89
+ env.write(f"PYTEST_VERBOSITY={pytest_verbosity}\n")
90
+ env.write(f"COV_REPORT_FILE={cov_report_file}\n")
91
+ env.write(f"COV_FAIL_UNDER={cov_fail_under}\n")
92
+ env.write(f"BANDIT_SKIP_FLAG={bandit_skip_flag}\n")
@@ -23,7 +23,7 @@ jobs:
23
23
 
24
24
  steps:
25
25
  - name: Checkout repository
26
- uses: actions/checkout@v5
26
+ uses: actions/checkout@v6
27
27
 
28
28
  - name: Initialize CodeQL
29
29
  uses: github/codeql-action/init@v4
@@ -0,0 +1,386 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main, master ]
6
+ pull_request:
7
+ branches: [ main, master ]
8
+ schedule:
9
+ # Run daily at 3:17 AM UTC (off-peak hours, odd minute to avoid congestion)
10
+ - cron: '17 3 * * *'
11
+
12
+ jobs:
13
+ setup:
14
+ name: Extract Python versions and OS matrix from pyproject.toml
15
+ runs-on: ubuntu-latest
16
+ outputs:
17
+ python-versions: ${{ steps.metadata.outputs.python-versions }}
18
+ python-latest: ${{ steps.metadata.outputs.python-latest }}
19
+ os-matrix: ${{ steps.metadata.outputs.os-matrix }}
20
+ steps:
21
+ - uses: actions/checkout@v6
22
+ - uses: actions/setup-python@v6
23
+ with:
24
+ python-version: "3.x"
25
+ - name: Parse pyproject.toml for Python versions and OS matrix
26
+ id: metadata
27
+ shell: python
28
+ run: |
29
+ import json, os, tomllib
30
+
31
+ with open("pyproject.toml", "rb") as f:
32
+ data = tomllib.load(f)
33
+
34
+ # Python versions from classifiers
35
+ classifiers = data.get("project", {}).get("classifiers", [])
36
+ prefix = "Programming Language :: Python :: "
37
+ versions = [
38
+ c[len(prefix):]
39
+ for c in classifiers
40
+ if c.startswith(prefix) and "." in c[len(prefix):]
41
+ ]
42
+ if not versions:
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
+
49
+ with open(os.environ["GITHUB_OUTPUT"], "a") as fh:
50
+ fh.write(f"python-versions={json.dumps(versions)}\n")
51
+ fh.write(f"python-latest={versions[-1]}\n")
52
+ fh.write(f"os-matrix={json.dumps(os_list)}\n")
53
+
54
+ test:
55
+ name: Tests (Python ${{ matrix.python }}, ${{ matrix.os }})
56
+ needs: setup
57
+ runs-on: ${{ matrix.os }}
58
+ strategy:
59
+ fail-fast: false
60
+ matrix:
61
+ os: ${{ fromJSON(needs.setup.outputs.os-matrix) }}
62
+ python: ${{ fromJSON(needs.setup.outputs.python-versions) }}
63
+ env:
64
+ PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1"
65
+ PYTHONIOENCODING: "utf-8"
66
+ UV_LINK_MODE: "copy"
67
+ UV_BREAK_SYSTEM_PACKAGES: "1"
68
+ steps:
69
+ - uses: actions/checkout@v6
70
+ - uses: actions/setup-python@v6
71
+ with:
72
+ python-version: ${{ matrix.python }}
73
+ - name: Install uv
74
+ uses: astral-sh/setup-uv@v7
75
+ with:
76
+ enable-cache: true
77
+ prune-cache: false
78
+
79
+ - name: Install bash 4+ on macOS
80
+ if: runner.os == 'macOS'
81
+ run: |
82
+ brew install bash
83
+ echo "$(brew --prefix)/bin" >> "$GITHUB_PATH"
84
+
85
+ - name: Extract project metadata
86
+ id: metadata
87
+ uses: ./.github/actions/extract-metadata
88
+
89
+ - name: Install CI system dependencies (Ubuntu)
90
+ if: runner.os == 'Linux'
91
+ shell: python
92
+ run: |
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.")
125
+
126
+ - name: Install dev deps
127
+ shell: bash
128
+ run: |
129
+ uv pip install -e .[dev] --system
130
+
131
+ - name: Install CI system dependencies (Windows/choco)
132
+ if: runner.os == 'Windows'
133
+ shell: python
134
+ run: |
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.")
166
+
167
+ # Self-hosted runners persist ruff cache via RUFF_CACHE_DIR volume mount,
168
+ # so actions/cache is only needed on GitHub-hosted runners.
169
+ - name: Restore ruff cache
170
+ if: runner.environment == 'github-hosted'
171
+ id: ruff-cache
172
+ uses: actions/cache/restore@v5
173
+ with:
174
+ path: .ruff_cache
175
+ key: ruff-${{ runner.os }}-py${{ matrix.python }}-${{ github.sha }}
176
+ restore-keys: |
177
+ ruff-${{ runner.os }}-py${{ matrix.python }}-
178
+ ruff-${{ runner.os }}-
179
+
180
+ - name: Run ruff (lint + format)
181
+ id: ruff
182
+ continue-on-error: true
183
+ shell: bash
184
+ run: |
185
+ ruff check .
186
+ ruff format --check .
187
+
188
+ - name: Run pyright (type check)
189
+ id: pyright
190
+ continue-on-error: true
191
+ shell: bash
192
+ run: pyright
193
+
194
+ - name: Run pytest (tests + coverage)
195
+ id: pytest
196
+ continue-on-error: true
197
+ shell: bash
198
+ env:
199
+ TEST_VERBOSE: "1"
200
+ # Write coverage SQLite database to local disk — network mounts
201
+ # and stale journal files cause "database is locked" errors.
202
+ COVERAGE_FILE: "${{ runner.temp }}/.coverage"
203
+ run: >-
204
+ pytest -m "not local_only"
205
+ --cov=${{ env.SRC_PATH }}/${{ env.PACKAGE_MODULE }}
206
+ --cov-report=term-missing
207
+ --cov-report=xml:${{ env.COV_REPORT_FILE }}
208
+ --cov-fail-under=${{ env.COV_FAIL_UNDER }}
209
+ ${{ env.PYTEST_VERBOSITY }}
210
+
211
+ - name: Upload coverage to Codecov
212
+ if: >-
213
+ always()
214
+ && steps.pytest.outcome == 'success'
215
+ && matrix.os == 'ubuntu-latest'
216
+ && matrix.python == needs.setup.outputs.python-latest
217
+ uses: codecov/codecov-action@v7
218
+ with:
219
+ files: ${{ env.COV_REPORT_FILE }}
220
+ fail_ci_if_error: false
221
+
222
+ - name: Run import-linter (architecture)
223
+ id: importlinter
224
+ continue-on-error: true
225
+ shell: bash
226
+ run: lint-imports
227
+
228
+ - name: Run bandit (security scan)
229
+ id: bandit
230
+ continue-on-error: true
231
+ shell: bash
232
+ run: bandit -q -c pyproject.toml -r ${{ env.SRC_PATH }}/${{ env.PACKAGE_MODULE }} ${{ env.BANDIT_SKIP_FLAG }}
233
+
234
+ - name: Run pip-audit (vulnerability scan)
235
+ id: pipaudit
236
+ continue-on-error: true
237
+ shell: python
238
+ run: |
239
+ import subprocess, sys
240
+ import rtoml
241
+ from pathlib import Path
242
+ data = rtoml.load(Path("pyproject.toml"))
243
+ vulns = data.get("tool", {}).get("pip-audit", {}).get("ignore-vulns", [])
244
+ cmd = ["pip-audit", "--skip-editable"]
245
+ for v in vulns:
246
+ cmd.extend(["--ignore-vuln", v])
247
+ sys.exit(subprocess.call(cmd))
248
+
249
+ - name: Check all steps passed
250
+ shell: bash
251
+ run: |
252
+ if [[ "${{ steps.ruff.outcome }}" != "success" ]]; then
253
+ echo "::error::Ruff check failed"
254
+ exit 1
255
+ fi
256
+ if [[ "${{ steps.pyright.outcome }}" != "success" ]]; then
257
+ echo "::error::Pyright check failed"
258
+ exit 1
259
+ fi
260
+ if [[ "${{ steps.pytest.outcome }}" != "success" ]]; then
261
+ echo "::error::Pytest failed"
262
+ exit 1
263
+ fi
264
+ if [[ "${{ steps.importlinter.outcome }}" != "success" ]]; then
265
+ echo "::error::Import-linter check failed"
266
+ exit 1
267
+ fi
268
+ if [[ "${{ steps.bandit.outcome }}" != "success" ]]; then
269
+ echo "::error::Bandit security scan failed"
270
+ exit 1
271
+ fi
272
+ if [[ "${{ steps.pipaudit.outcome }}" != "success" ]]; then
273
+ echo "::warning::pip-audit vulnerability scan failed"
274
+ fi
275
+ echo "All checks passed!"
276
+
277
+ - name: Build wheel/sdist
278
+ shell: bash
279
+ run: uv build
280
+
281
+ - name: Verify wheel install in clean env
282
+ shell: bash
283
+ run: |
284
+ uv venv .venv_wheel
285
+ . .venv_wheel/bin/activate 2>/dev/null || . .venv_wheel/Scripts/activate 2>/dev/null
286
+ uv pip install dist/*.whl
287
+ "$CLI_BIN" --version 2>/dev/null || python -m "$PACKAGE_MODULE" --version
288
+
289
+ - name: Prune uv cache
290
+ continue-on-error: true
291
+ shell: bash
292
+ run: uv cache prune --ci
293
+
294
+ - name: Save ruff cache
295
+ if: always() && runner.environment == 'github-hosted' && steps.ruff-cache.outputs.cache-hit != 'true'
296
+ uses: actions/cache/save@v5
297
+ with:
298
+ path: .ruff_cache
299
+ key: ruff-${{ runner.os }}-py${{ matrix.python }}-${{ github.sha }}
300
+
301
+ pipx-uv:
302
+ name: pipx/uv verification (ubuntu)
303
+ runs-on: ubuntu-latest
304
+ steps:
305
+ - uses: actions/checkout@v6
306
+ - uses: actions/setup-python@v6
307
+ with:
308
+ python-version: "3.14"
309
+
310
+ - name: Install uv
311
+ uses: astral-sh/setup-uv@v7
312
+ with:
313
+ enable-cache: true
314
+ prune-cache: false
315
+
316
+ - name: Extract project metadata
317
+ id: metadata
318
+ uses: ./.github/actions/extract-metadata
319
+
320
+ - name: Build wheel
321
+ run: uv build
322
+ - name: pipx install from wheel
323
+ run: |
324
+ uv pip install --system pipx
325
+ pipx ensurepath
326
+ export PATH="$HOME/.local/bin:$PATH"
327
+ pipx install dist/*.whl
328
+ "$CLI_BIN" --version
329
+
330
+ - name: uv tool install
331
+ run: |
332
+ export PATH="$HOME/.local/bin:$PATH"
333
+ uv tool install --force .
334
+ "$CLI_BIN" --version
335
+
336
+ - name: Prune uv cache
337
+ continue-on-error: true
338
+ run: uv cache prune --ci
339
+
340
+ notebooks:
341
+ name: Execute notebooks (ubuntu, Python 3.14)
342
+ runs-on: ubuntu-latest
343
+ steps:
344
+ - uses: actions/checkout@v6
345
+ - uses: actions/setup-python@v6
346
+ with:
347
+ python-version: "3.14"
348
+
349
+ - name: Install uv
350
+ uses: astral-sh/setup-uv@v7
351
+ with:
352
+ enable-cache: true
353
+ prune-cache: false
354
+
355
+ - name: Install notebook runner deps
356
+ run: |
357
+ uv pip install --system nbclient nbformat ipykernel jupyter_client
358
+ python -m ipykernel install --user --name python3 --display-name "Python 3"
359
+ - name: Execute Quickstart notebook
360
+ env:
361
+ PIP_DISABLE_PIP_VERSION_CHECK: "1"
362
+ PIP_NO_INPUT: "1"
363
+ run: |
364
+ python - << 'PY'
365
+ from pathlib import Path
366
+
367
+ import nbformat
368
+ from nbclient import NotebookClient
369
+
370
+ nb_path = Path('notebooks/Quickstart.ipynb')
371
+ if not nb_path.exists():
372
+ raise SystemExit(f"Notebook not found: {nb_path}")
373
+
374
+ notebook = nbformat.read(nb_path, as_version=4)
375
+ client = NotebookClient(
376
+ notebook,
377
+ timeout=900,
378
+ kernel_name='python3',
379
+ allow_errors=False,
380
+ )
381
+ client.execute()
382
+
383
+ out_path = Path('notebooks/Quickstart-executed.ipynb')
384
+ nbformat.write(notebook, out_path)
385
+ print(f"Executed notebook written to: {out_path}")
386
+ PY
@@ -9,42 +9,61 @@ on:
9
9
  workflow_dispatch: {}
10
10
 
11
11
  jobs:
12
- build-and-publish:
12
+ build:
13
13
  runs-on: ubuntu-latest
14
14
  permissions:
15
15
  contents: read
16
+ env:
17
+ UV_LINK_MODE: "copy"
16
18
  steps:
17
- - uses: actions/checkout@v5
18
- - name: Verify PyPI token is set
19
- run: |
20
- if [ -z "${{ secrets.PYPI_API_TOKEN }}" ]; then
21
- echo "::error title=Missing PyPI token::Set the PYPI_API_TOKEN secret in GitHub repository settings to enable PyPI publishing on releases."
22
- echo "See: https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions"
23
- exit 1
24
- fi
19
+ - uses: actions/checkout@v6
25
20
  - uses: actions/setup-python@v6
26
21
  with:
27
- python-version: '3.13'
28
- cache: 'pip'
29
- cache-dependency-path: 'pyproject.toml'
22
+ python-version: '3.14'
23
+
30
24
  - name: Install uv
31
25
  uses: astral-sh/setup-uv@v7
32
26
  with:
33
27
  enable-cache: true
28
+ prune-cache: false
29
+
34
30
  - name: Build artifacts
31
+ run: uv build
32
+
33
+ - name: Upload artifacts
34
+ uses: actions/upload-artifact@v7
35
+ with:
36
+ name: dist
37
+ path: dist/*
38
+
39
+ - name: Prune uv cache
40
+ continue-on-error: true
41
+ run: uv cache prune --ci
42
+
43
+ publish:
44
+ needs: build
45
+ runs-on: ubuntu-latest
46
+ environment: pypi
47
+ permissions:
48
+ contents: read
49
+ steps:
50
+ - name: Verify PyPI token is set
35
51
  run: |
36
- python -m pip install --upgrade pip
37
- uv pip install build --system
38
- python -m build
52
+ if [ -z "${{ secrets.PYPI_API_TOKEN }}" ]; then
53
+ echo "::error title=Missing PyPI token::Set the PYPI_API_TOKEN secret in GitHub repository settings to enable PyPI publishing on releases."
54
+ echo "See: https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions"
55
+ exit 1
56
+ fi
57
+
58
+ - name: Download artifacts
59
+ uses: actions/download-artifact@v8
60
+ with:
61
+ name: dist
62
+ path: dist/
63
+
39
64
  - name: Publish to PyPI
40
- # Use the rolling major tag to get latest Twine/standards support (e.g., Core Metadata 2.4)
41
65
  uses: pypa/gh-action-pypi-publish@release/v1
42
66
  with:
43
67
  password: ${{ secrets.PYPI_API_TOKEN }}
44
68
  attestations: false
45
69
  skip-existing: true
46
- - name: Upload artifacts
47
- uses: actions/upload-artifact@v5
48
- with:
49
- name: dist
50
- path: dist/*
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [3.0.2] 2026-06-14
11
+
12
+ ### Changed
13
+ - 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 (`reportUnknownMemberType`). The repo-wide `reportUnknownMemberType` suppression is retained, but only for the `tomli` backport (untyped on Python 3.10); the rich-click decorators no longer rely on it.
14
+ - Migrated build automation from the in-repo `scripts/` package to the external `bmk` tooling (`uvx bmk`); removed the obsolete `scripts/` directory and `[tool.scripts.test]` config; added `[tool.bashate]` settings and the bmk CI workflows.
15
+
16
+ ### Removed
17
+ - Dropped Python 3.9 support (`requires-python>=3.10`): the bmk build/dev toolchain requires Python 3.10+.
18
+
19
+ ## [3.0.1] - 2025-12-15
20
+
21
+ ### Changed
22
+
23
+ - Replaced `tomllib`/`tomli` with `rtoml` for TOML parsing in scripts
24
+ - Updated GitHub Actions dependencies:
25
+ - `actions/cache` v4 → v5
26
+ - `actions/upload-artifact` v5 → v6
27
+ - Added `PYTHONIOENCODING: "utf-8"` environment variable to CI workflow for consistent encoding
28
+
8
29
  ## [3.0.0] - 2025-11-04
9
30
 
10
31
  ### Added