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.
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.devcontainer/devcontainer.json +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.devcontainer/settings.json +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.env.example +0 -0
- lib_detect_testenv-3.0.2/.github/actions/extract-metadata/action.yml +92 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.github/dependabot.yml +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.github/workflows/codeql.yml +1 -1
- lib_detect_testenv-3.0.2/.github/workflows/default_cicd_public.yml +386 -0
- lib_detect_testenv-3.0.0/.github/workflows/release.yml → lib_detect_testenv-3.0.2/.github/workflows/default_release_public.yml +40 -21
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.gitignore +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.qlty/qlty.toml +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/.snyk +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/CHANGELOG.md +21 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/CLAUDE.md +47 -14
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/CONTRIBUTING.md +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/DEVELOPMENT.md +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/INSTALL.md +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/LICENSE +0 -0
- lib_detect_testenv-3.0.2/Makefile +260 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/PKG-INFO +19 -16
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/README.md +1 -1
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/codecov.yml +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/concept.md +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/docs/systemdesign/module_reference.md +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/notebooks/Quickstart.ipynb +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/pyproject.toml +63 -20
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/reset_git_history.sh +16 -15
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/__init__.py +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/__init__conf__.py +8 -1
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/__main__.py +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/behaviors.py +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/cli.py +12 -11
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/lib_detect_testenv.py +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/src/lib_detect_testenv/py.typed +0 -0
- lib_detect_testenv-3.0.2/src/lib_detect_testenv/typed_click.py +37 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/tests/conftest.py +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/tests/test_behaviors.py +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/tests/test_cli.py +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/tests/test_lib_detect_testenv.py +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/tests/test_metadata.py +0 -0
- {lib_detect_testenv-3.0.0 → lib_detect_testenv-3.0.2}/tests/test_module_entry.py +0 -0
- lib_detect_testenv-3.0.0/.github/workflows/ci.yml +0 -208
- lib_detect_testenv-3.0.0/AGENTS.md +0 -104
- lib_detect_testenv-3.0.0/Makefile +0 -54
- lib_detect_testenv-3.0.0/README.rst.bak +0 -480
- lib_detect_testenv-3.0.0/scripts/__init__.py +0 -3
- lib_detect_testenv-3.0.0/scripts/__main__.py +0 -7
- lib_detect_testenv-3.0.0/scripts/_utils.py +0 -591
- lib_detect_testenv-3.0.0/scripts/build.py +0 -52
- lib_detect_testenv-3.0.0/scripts/bump.py +0 -32
- lib_detect_testenv-3.0.0/scripts/bump_major.py +0 -20
- lib_detect_testenv-3.0.0/scripts/bump_minor.py +0 -20
- lib_detect_testenv-3.0.0/scripts/bump_patch.py +0 -20
- lib_detect_testenv-3.0.0/scripts/bump_version.py +0 -80
- lib_detect_testenv-3.0.0/scripts/clean.py +0 -48
- lib_detect_testenv-3.0.0/scripts/cli.py +0 -213
- lib_detect_testenv-3.0.0/scripts/dev.py +0 -19
- lib_detect_testenv-3.0.0/scripts/help.py +0 -41
- lib_detect_testenv-3.0.0/scripts/install.py +0 -19
- lib_detect_testenv-3.0.0/scripts/menu.py +0 -568
- lib_detect_testenv-3.0.0/scripts/push.py +0 -77
- lib_detect_testenv-3.0.0/scripts/release.py +0 -90
- lib_detect_testenv-3.0.0/scripts/run_cli.py +0 -154
- lib_detect_testenv-3.0.0/scripts/target_metadata.py +0 -174
- lib_detect_testenv-3.0.0/scripts/test.py +0 -545
- lib_detect_testenv-3.0.0/scripts/version_current.py +0 -20
- lib_detect_testenv-3.0.0/tests/test_scripts.py +0 -266
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -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")
|
|
File without changes
|
|
@@ -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
|
|
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@
|
|
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.
|
|
28
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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/*
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -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
|