lib-detect-testenv 3.0.2__tar.gz → 3.0.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.
Files changed (45) hide show
  1. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/.devcontainer/devcontainer.json +0 -0
  2. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/.devcontainer/settings.json +0 -0
  3. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/.env.example +0 -0
  4. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/.github/actions/extract-metadata/action.yml +16 -0
  5. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/.github/dependabot.yml +0 -0
  6. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/.github/workflows/codeql.yml +1 -1
  7. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/.github/workflows/default_cicd_public.yml +63 -13
  8. lib_detect_testenv-3.0.4/.github/workflows/default_release_public.yml +95 -0
  9. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/.gitignore +6 -0
  10. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/.qlty/qlty.toml +0 -0
  11. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/.snyk +0 -0
  12. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/CHANGELOG.md +35 -0
  13. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/CLAUDE.md +5 -5
  14. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/CONTRIBUTING.md +0 -0
  15. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/DEVELOPMENT.md +0 -0
  16. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/INSTALL.md +0 -0
  17. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/LICENSE +0 -0
  18. lib_detect_testenv-3.0.4/Makefile +453 -0
  19. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/PKG-INFO +21 -21
  20. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/README.md +12 -12
  21. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/codecov.yml +0 -0
  22. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/concept.md +9 -9
  23. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/docs/systemdesign/module_reference.md +1 -0
  24. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/notebooks/Quickstart.ipynb +4 -4
  25. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/pyproject.toml +51 -9
  26. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/src/lib_detect_testenv/__init__.py +10 -10
  27. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/src/lib_detect_testenv/__init__conf__.py +4 -2
  28. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/src/lib_detect_testenv/__main__.py +0 -0
  29. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/src/lib_detect_testenv/behaviors.py +5 -7
  30. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/src/lib_detect_testenv/cli.py +33 -30
  31. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/src/lib_detect_testenv/lib_detect_testenv.py +5 -12
  32. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/src/lib_detect_testenv/py.typed +0 -0
  33. lib_detect_testenv-3.0.4/src/lib_detect_testenv/safe_console.py +213 -0
  34. lib_detect_testenv-3.0.4/src/lib_detect_testenv/typed_click.py +51 -0
  35. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/tests/conftest.py +4 -1
  36. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/tests/test_behaviors.py +0 -0
  37. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/tests/test_cli.py +7 -3
  38. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/tests/test_lib_detect_testenv.py +1 -1
  39. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/tests/test_metadata.py +12 -12
  40. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/tests/test_module_entry.py +4 -1
  41. lib_detect_testenv-3.0.4/tests/test_safe_console.py +113 -0
  42. lib_detect_testenv-3.0.2/.github/workflows/default_release_public.yml +0 -69
  43. lib_detect_testenv-3.0.2/Makefile +0 -260
  44. lib_detect_testenv-3.0.2/src/lib_detect_testenv/typed_click.py +0 -37
  45. {lib_detect_testenv-3.0.2 → lib_detect_testenv-3.0.4}/reset_git_history.sh +0 -0
@@ -29,6 +29,11 @@ outputs:
29
29
  BANDIT_SKIP_FLAG:
30
30
  description: Bandit skip flag string (e.g. "-s B101,B601") or empty
31
31
  value: ${{ steps.meta.outputs.BANDIT_SKIP_FLAG }}
32
+ PYTEST_MARKER_EXPR:
33
+ description: >-
34
+ Marker expression for the CI test run (e.g. "not local_only and not integration"),
35
+ built from tool.ci.pytest-exclude-markers
36
+ value: ${{ steps.meta.outputs.PYTEST_MARKER_EXPR }}
32
37
 
33
38
  runs:
34
39
  using: composite
@@ -69,6 +74,15 @@ runs:
69
74
  bandit_skips = data.get('tool', {}).get('bandit', {}).get('skips', [])
70
75
  bandit_skip_flag = f"-s {','.join(bandit_skips)}" if bandit_skips else ""
71
76
 
77
+ # --- [tool.ci] section ---
78
+ # CI always skips local_only (those tests need a dev box). A project whose
79
+ # suite also carries markers CI cannot satisfy - integration tests needing a
80
+ # container that a separate workflow starts - names them here, rather than
81
+ # every such repo forking this workflow to edit one -m expression.
82
+ ci_config = data.get('tool', {}).get('ci', {})
83
+ exclude_markers = ['local_only', *ci_config.get('pytest-exclude-markers', [])]
84
+ pytest_marker_expr = ' and '.join(f'not {m}' for m in exclude_markers)
85
+
72
86
  # Write to GITHUB_OUTPUT for composite action outputs
73
87
  with open(os.environ['GITHUB_OUTPUT'], 'a', encoding='utf-8') as out:
74
88
  out.write(f"PROJECT_NAME={project}\n")
@@ -79,6 +93,7 @@ runs:
79
93
  out.write(f"COV_REPORT_FILE={cov_report_file}\n")
80
94
  out.write(f"COV_FAIL_UNDER={cov_fail_under}\n")
81
95
  out.write(f"BANDIT_SKIP_FLAG={bandit_skip_flag}\n")
96
+ out.write(f"PYTEST_MARKER_EXPR={pytest_marker_expr}\n")
82
97
 
83
98
  # Also write to GITHUB_ENV so subsequent steps can use ${{ env.VAR }}
84
99
  with open(os.environ['GITHUB_ENV'], 'a', encoding='utf-8') as env:
@@ -90,3 +105,4 @@ runs:
90
105
  env.write(f"COV_REPORT_FILE={cov_report_file}\n")
91
106
  env.write(f"COV_FAIL_UNDER={cov_fail_under}\n")
92
107
  env.write(f"BANDIT_SKIP_FLAG={bandit_skip_flag}\n")
108
+ env.write(f"PYTEST_MARKER_EXPR={pytest_marker_expr}\n")
@@ -23,7 +23,7 @@ jobs:
23
23
 
24
24
  steps:
25
25
  - name: Checkout repository
26
- uses: actions/checkout@v6
26
+ uses: actions/checkout@v7
27
27
 
28
28
  - name: Initialize CodeQL
29
29
  uses: github/codeql-action/init@v4
@@ -18,8 +18,8 @@ jobs:
18
18
  python-latest: ${{ steps.metadata.outputs.python-latest }}
19
19
  os-matrix: ${{ steps.metadata.outputs.os-matrix }}
20
20
  steps:
21
- - uses: actions/checkout@v6
22
- - uses: actions/setup-python@v6
21
+ - uses: actions/checkout@v7
22
+ - uses: actions/setup-python@v7
23
23
  with:
24
24
  python-version: "3.x"
25
25
  - name: Parse pyproject.toml for Python versions and OS matrix
@@ -66,8 +66,8 @@ jobs:
66
66
  UV_LINK_MODE: "copy"
67
67
  UV_BREAK_SYSTEM_PACKAGES: "1"
68
68
  steps:
69
- - uses: actions/checkout@v6
70
- - uses: actions/setup-python@v6
69
+ - uses: actions/checkout@v7
70
+ - uses: actions/setup-python@v7
71
71
  with:
72
72
  python-version: ${{ matrix.python }}
73
73
  - name: Install uv
@@ -169,7 +169,7 @@ jobs:
169
169
  - name: Restore ruff cache
170
170
  if: runner.environment == 'github-hosted'
171
171
  id: ruff-cache
172
- uses: actions/cache/restore@v5
172
+ uses: actions/cache/restore@v6
173
173
  with:
174
174
  path: .ruff_cache
175
175
  key: ruff-${{ runner.os }}-py${{ matrix.python }}-${{ github.sha }}
@@ -201,7 +201,7 @@ jobs:
201
201
  # and stale journal files cause "database is locked" errors.
202
202
  COVERAGE_FILE: "${{ runner.temp }}/.coverage"
203
203
  run: >-
204
- pytest -m "not local_only"
204
+ pytest -m "${{ env.PYTEST_MARKER_EXPR }}"
205
205
  --cov=${{ env.SRC_PATH }}/${{ env.PACKAGE_MODULE }}
206
206
  --cov-report=term-missing
207
207
  --cov-report=xml:${{ env.COV_REPORT_FILE }}
@@ -236,12 +236,62 @@ jobs:
236
236
  continue-on-error: true
237
237
  shell: python
238
238
  run: |
239
- import subprocess, sys
239
+ import subprocess, sys, tempfile
240
240
  import rtoml
241
241
  from pathlib import Path
242
+
243
+ # Audit the project's OWN resolved dependency tree, not the ambient
244
+ # environment. "Install dev deps" uses --system, so the interpreter
245
+ # being audited is the runner image's, and that image ships packages
246
+ # this project neither declares nor distributes (setuptools, pip,
247
+ # wheel). Auditing those reported CVEs no consumer of this project is
248
+ # exposed to, and a scan that is permanently yellow is a scan everyone
249
+ # learns to ignore. Resolving pyproject.toml here keeps the result
250
+ # identical on every OS/python and dependent only on what we declare.
242
251
  data = rtoml.load(Path("pyproject.toml"))
252
+
253
+ # Not every project declares dev dependencies, and those that do may use
254
+ # either an extra or a PEP 735 group. Asking uv for one that does not
255
+ # exist is a hard error ("Requested extra not found"), which would leave
256
+ # the audit permanently broken here rather than merely noisy, so select
257
+ # the flag from what this project actually declares.
258
+ selector = []
259
+ if "dev" in data.get("project", {}).get("optional-dependencies", {}):
260
+ selector = ["--extra", "dev"]
261
+ elif "dev" in data.get("dependency-groups", {}):
262
+ selector = ["--group", "dev"]
263
+
264
+ tmp = Path(tempfile.mkdtemp())
265
+ req = tmp / "compiled-requirements.txt"
266
+ subprocess.check_call(
267
+ ["uv", "pip", "compile", *selector, "pyproject.toml", "-o", str(req), "--quiet"]
268
+ )
269
+
270
+ # Drop direct URL references ("pkg @ git+https://...") before auditing.
271
+ # pip-audit cannot audit those (they are not on PyPI) but, given a
272
+ # requirements file, first tries to `git clone` each one to resolve a
273
+ # version - a slow, credential-dependent fetch. No public project here
274
+ # uses one today, yet "allow-direct-references" is enabled, so this
275
+ # guards the day one appears and keeps this step identical to the
276
+ # default_cicd_private template's.
277
+ audit_req = tmp / "audit-requirements.txt"
278
+ kept, dropped = [], []
279
+ for line in req.read_text(encoding="utf-8").splitlines():
280
+ if " @ " in line and not line.lstrip().startswith("#"):
281
+ dropped.append(line.split(" @ ", 1)[0].strip())
282
+ else:
283
+ kept.append(line)
284
+ audit_req.write_text("\n".join(kept) + "\n", encoding="utf-8")
285
+ if dropped:
286
+ # Never drop coverage silently: name what is not being audited here.
287
+ print(f"::notice::pip-audit: not auditing {len(dropped)} direct-URL dependency/ies "
288
+ f"(audited in their own repo): {', '.join(sorted(dropped))}")
289
+
290
+ req = audit_req
243
291
  vulns = data.get("tool", {}).get("pip-audit", {}).get("ignore-vulns", [])
244
- cmd = ["pip-audit", "--skip-editable"]
292
+ # --no-deps: the compiled file is already a complete pinned resolution,
293
+ # so pip-audit must audit it verbatim rather than re-resolving it.
294
+ cmd = ["pip-audit", "--no-deps", "-r", str(req)]
245
295
  for v in vulns:
246
296
  cmd.extend(["--ignore-vuln", v])
247
297
  sys.exit(subprocess.call(cmd))
@@ -293,7 +343,7 @@ jobs:
293
343
 
294
344
  - name: Save ruff cache
295
345
  if: always() && runner.environment == 'github-hosted' && steps.ruff-cache.outputs.cache-hit != 'true'
296
- uses: actions/cache/save@v5
346
+ uses: actions/cache/save@v6
297
347
  with:
298
348
  path: .ruff_cache
299
349
  key: ruff-${{ runner.os }}-py${{ matrix.python }}-${{ github.sha }}
@@ -302,8 +352,8 @@ jobs:
302
352
  name: pipx/uv verification (ubuntu)
303
353
  runs-on: ubuntu-latest
304
354
  steps:
305
- - uses: actions/checkout@v6
306
- - uses: actions/setup-python@v6
355
+ - uses: actions/checkout@v7
356
+ - uses: actions/setup-python@v7
307
357
  with:
308
358
  python-version: "3.14"
309
359
 
@@ -341,8 +391,8 @@ jobs:
341
391
  name: Execute notebooks (ubuntu, Python 3.14)
342
392
  runs-on: ubuntu-latest
343
393
  steps:
344
- - uses: actions/checkout@v6
345
- - uses: actions/setup-python@v6
394
+ - uses: actions/checkout@v7
395
+ - uses: actions/setup-python@v7
346
396
  with:
347
397
  python-version: "3.14"
348
398
 
@@ -0,0 +1,95 @@
1
+ name: Release
2
+
3
+ # The tag push is the only release event bmk guarantees: it runs
4
+ # `git push <remote> <tag>` unconditionally, whereas `gh release create` sits
5
+ # behind a gh-availability check and runs with check=False, so a missing or
6
+ # failing gh would leave nothing to publish. Listening for `release: published`
7
+ # as well fired a second, redundant run that raced the first to the upload and
8
+ # only ever logged "Skipping ... already exists".
9
+ on:
10
+ push:
11
+ tags:
12
+ - 'v*'
13
+ workflow_dispatch: {}
14
+
15
+ # One publish at a time per tag, so a workflow_dispatch re-run cannot overlap
16
+ # the run the tag push started.
17
+ concurrency:
18
+ group: release-${{ github.ref_name }}
19
+ cancel-in-progress: false
20
+
21
+ jobs:
22
+ build:
23
+ runs-on: ubuntu-latest
24
+ permissions:
25
+ contents: read
26
+ env:
27
+ UV_LINK_MODE: "copy"
28
+ steps:
29
+ - uses: actions/checkout@v7
30
+ - uses: actions/setup-python@v7
31
+ with:
32
+ python-version: '3.14'
33
+
34
+ - name: Install uv
35
+ uses: astral-sh/setup-uv@v7
36
+ with:
37
+ enable-cache: true
38
+ prune-cache: false
39
+
40
+ - name: Build artifacts
41
+ run: uv build
42
+
43
+ - name: Upload artifacts
44
+ uses: actions/upload-artifact@v7
45
+ with:
46
+ name: dist
47
+ path: dist/*
48
+
49
+ - name: Prune uv cache
50
+ continue-on-error: true
51
+ run: uv cache prune --ci
52
+
53
+ publish:
54
+ needs: build
55
+ runs-on: ubuntu-latest
56
+ environment: pypi
57
+ permissions:
58
+ contents: read
59
+ # id-token is required for OIDC / PyPI Trusted Publisher. It is harmless when
60
+ # a PYPI_API_TOKEN secret is configured, in which case the token path runs.
61
+ id-token: write
62
+ steps:
63
+ - name: Select PyPI auth method
64
+ id: auth
65
+ env:
66
+ PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
67
+ run: |
68
+ if [ -n "$PYPI_API_TOKEN" ]; then
69
+ echo "PYPI_API_TOKEN is set; publishing with the API token."
70
+ echo "method=token" >> "$GITHUB_OUTPUT"
71
+ else
72
+ echo "No PYPI_API_TOKEN; publishing via OIDC Trusted Publisher."
73
+ echo "method=oidc" >> "$GITHUB_OUTPUT"
74
+ fi
75
+
76
+ - name: Download artifacts
77
+ uses: actions/download-artifact@v8
78
+ with:
79
+ name: dist
80
+ path: dist/
81
+
82
+ - name: Publish to PyPI (API token)
83
+ if: steps.auth.outputs.method == 'token'
84
+ uses: pypa/gh-action-pypi-publish@release/v1
85
+ with:
86
+ password: ${{ secrets.PYPI_API_TOKEN }}
87
+ attestations: false
88
+ skip-existing: true
89
+
90
+ - name: Publish to PyPI (Trusted Publisher / OIDC)
91
+ if: steps.auth.outputs.method == 'oidc'
92
+ uses: pypa/gh-action-pypi-publish@release/v1
93
+ with:
94
+ attestations: true
95
+ skip-existing: true
@@ -179,3 +179,9 @@ result
179
179
  poetry.lock
180
180
  pdm.lock
181
181
  uv.lock
182
+
183
+ # Local, per-developer settings (Claude Code etc.) - never commit
184
+ *.local.json
185
+
186
+ # Python virtual environments (managed by bmk)
187
+ .venv*/
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.0.4] 2026-08-01 00:14:59
11
+ ### Fixed
12
+ - **Console output no longer crashes on a legacy codepage.** A Windows console at codepage 1252
13
+ hands Python an `errors="strict"` stream, so writing a non-ASCII character such as a check mark
14
+ raised `UnicodeEncodeError: 'charmap' codec can't encode character` and the command exited
15
+ non-zero *after* its work had already succeeded. Click does not protect against this, and Rich
16
+ raises the same way through its own writer. The new `safe_console` module degrades at the sink:
17
+ a UTF-8 terminal still receives the character, and only a stream that cannot encode it sees
18
+ `[OK]` / `[X]` / `[!]`. Every `click.echo` call site routes through it, and a test fails if a
19
+ module reaches for `click.echo` or builds an unwrapped `Console(file=sys.stdout)` again.
20
+
21
+
22
+ ## [3.0.3] 2026-07-24 16:01:54
23
+
24
+ ### Fixed
25
+ - Fixed CI going red under the latest ruff (0.16 widened its default rule set to ~920 rules on a
26
+ repo with no explicit `[tool.ruff.lint].select`): pinned the curated bitranox rule select instead
27
+ of chasing the exploded default set.
28
+ - `raise SystemExit(2)` in the CLI's error handlers now uses `from e` so tracebacks show the
29
+ original cause (B904).
30
+ - `print_info()` and the `__main__` guard now write via `sys.stdout`/`sys.stderr` instead of `print`,
31
+ fixing a latent bug where the guard printed a `bytes` literal's repr instead of the plain message.
32
+
33
+ ### Changed
34
+ - Added the curated `[tool.ruff.lint].select` (matching `bitranox_template_py_cli`) plus
35
+ `pydocstyle` Google convention and a `tests/*.py` per-file-ignore list, replacing the previous
36
+ no-`select` config that inherited ruff's entire default rule set.
37
+ - Made the Click CLI callback parameters keyword-only (`*,`) so ruff's `PLR0917` no longer fires;
38
+ Click already invokes command callbacks by keyword, so this is not a call-site change.
39
+ - Simplified boolean-returning `if/return True/False` branches in `lib_detect_testenv.py` to direct
40
+ `return <condition>` expressions (SIM103).
41
+ - Moved two deferred, no-reason `import sys`/`from rich.style import Style` statements in `cli.py`
42
+ to the module top (PLC0415).
43
+ - Replaced `Optional[X]` with `X | None` throughout `cli.py` (UP045).
44
+
10
45
  ## [3.0.2] 2026-06-14
11
46
 
12
47
  ### Changed
@@ -9,9 +9,9 @@ When starting a new session, read and apply the following system prompt files fr
9
9
 
10
10
  ### Bash-Specific Guidelines
11
11
  When working with Bash scripts:
12
- - use skill `bx-bash-reference` when in doubt of bash features or syntax
12
+ - use skill `bitranox:bash-reference` when in doubt of bash features or syntax
13
13
  - `core_programming_solid.md`
14
- - use skill `bx-bash-clean-architecture`
14
+ - use skill `bitranox:bash-clean-architecture`
15
15
  - `bash_clean_code.md`
16
16
  - `bash_small_functions.md`
17
17
 
@@ -19,10 +19,10 @@ When working with Bash scripts:
19
19
  When working with Python code:
20
20
  - `core_programming_solid.md`
21
21
  - `python_solid_architecture_enforcer.md`
22
- - use skill `bx-python-clean-architecture`
22
+ - use skill `bitranox:python-clean-architecture`
23
23
  - `python_clean_code.md`
24
24
  - `python_small_functions_style.md`
25
- - use skill `bx-python-libraries-to-use`
25
+ - use skill `bitranox:python-use-modern-libraries`
26
26
  - `python_structure_template.md`
27
27
 
28
28
  ### Additional Guidelines
@@ -137,7 +137,7 @@ Import rules (enforced by import-linter):
137
137
  - `cli` depends on `lib_detect_testenv` and `behaviors`
138
138
  - Core detection functions have no internal dependencies
139
139
 
140
- Apply principles use skill `bx-python-clean-architecture` when designing and implementing features.
140
+ Apply principles use skill `bitranox:python-clean-architecture` when designing and implementing features.
141
141
 
142
142
  ## Security & Configuration
143
143