lib-detect-testenv 3.0.5__tar.gz → 3.0.6__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.5 → lib_detect_testenv-3.0.6}/CHANGELOG.md +21 -0
  2. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/PKG-INFO +2 -1
  3. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/pyproject.toml +18 -1
  4. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/src/lib_detect_testenv/__init__conf__.py +1 -1
  5. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/tests/conftest.py +18 -0
  6. lib_detect_testenv-3.0.6/tests/test_packaging.py +127 -0
  7. lib_detect_testenv-3.0.6/tests/test_platform_markers.py +31 -0
  8. lib_detect_testenv-3.0.5/.devcontainer/devcontainer.json +0 -22
  9. lib_detect_testenv-3.0.5/.devcontainer/settings.json +0 -6
  10. lib_detect_testenv-3.0.5/.env.example +0 -11
  11. lib_detect_testenv-3.0.5/.github/actions/extract-metadata/action.yml +0 -108
  12. lib_detect_testenv-3.0.5/.github/dependabot.yml +0 -22
  13. lib_detect_testenv-3.0.5/.github/workflows/codeql.yml +0 -39
  14. lib_detect_testenv-3.0.5/.github/workflows/default_cicd_public.yml +0 -472
  15. lib_detect_testenv-3.0.5/.github/workflows/default_release_public.yml +0 -95
  16. lib_detect_testenv-3.0.5/.qlty/qlty.toml +0 -2
  17. lib_detect_testenv-3.0.5/.snyk +0 -5
  18. lib_detect_testenv-3.0.5/CLAUDE.md +0 -97
  19. lib_detect_testenv-3.0.5/CONTRIBUTING.md +0 -48
  20. lib_detect_testenv-3.0.5/DEVELOPMENT.md +0 -134
  21. lib_detect_testenv-3.0.5/INSTALL.md +0 -193
  22. lib_detect_testenv-3.0.5/Makefile +0 -487
  23. lib_detect_testenv-3.0.5/codecov.yml +0 -28
  24. lib_detect_testenv-3.0.5/concept.md +0 -312
  25. lib_detect_testenv-3.0.5/notebooks/Quickstart.ipynb +0 -165
  26. lib_detect_testenv-3.0.5/reset_git_history.sh +0 -55
  27. lib_detect_testenv-3.0.5/uv.lock +0 -1776
  28. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/.gitignore +0 -0
  29. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/LICENSE +0 -0
  30. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/README.md +0 -0
  31. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/docs/systemdesign/module_reference.md +0 -0
  32. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/src/lib_detect_testenv/__init__.py +0 -0
  33. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/src/lib_detect_testenv/__main__.py +0 -0
  34. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/src/lib_detect_testenv/behaviors.py +0 -0
  35. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/src/lib_detect_testenv/cli.py +0 -0
  36. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/src/lib_detect_testenv/lib_detect_testenv.py +0 -0
  37. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/src/lib_detect_testenv/py.typed +0 -0
  38. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/src/lib_detect_testenv/safe_console.py +0 -0
  39. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/src/lib_detect_testenv/typed_click.py +0 -0
  40. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/tests/test_behaviors.py +0 -0
  41. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/tests/test_cli.py +0 -0
  42. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/tests/test_lib_detect_testenv.py +0 -0
  43. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/tests/test_metadata.py +0 -0
  44. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/tests/test_module_entry.py +0 -0
  45. {lib_detect_testenv-3.0.5 → lib_detect_testenv-3.0.6}/tests/test_safe_console.py +0 -0
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.0.6] 2026-10-15 16:35:25
11
+
12
+ ### Fixed
13
+
14
+ - The source distribution ships only the package, its tests, its docs, `README.md`, `LICENSE`,
15
+ `CHANGELOG.md` and `pyproject.toml` (an include list). Earlier sdists also carried every other
16
+ root file and, in a local build, anything hidden only through `.git/info/exclude` or a nested
17
+ `.gitignore`. `tests/test_packaging.py` builds the sdist and wheel and checks both; `hatchling`
18
+ joins the dev extra for it.
19
+
20
+ ### Changed
21
+
22
+ - Ruff selects `C90` (mccabe), so a function over cyclomatic complexity 10, the house hard limit,
23
+ now fails `make test`; before, nothing enforced it.
24
+
25
+ ### Tests
26
+
27
+ - The `os_windows`, `os_macos`, `os_linux` and `os_posix` markers now skip a test off its platform
28
+ (`pytest_runtest_setup` in `tests/conftest.py`); registered in `pyproject.toml` alone they skipped
29
+ nothing, so a test trusting its marker ran everywhere. `tests/test_platform_markers.py` pins it.
30
+
10
31
  ## [3.0.5] 2026-10-15 14:11:33
11
32
 
12
33
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lib_detect_testenv
3
- Version: 3.0.5
3
+ Version: 3.0.6
4
4
  Summary: Detect test environment - pytest, doctest, unittest, or regular execution
5
5
  Project-URL: Homepage, https://github.com/bitranox/lib_detect_testenv
6
6
  Project-URL: Repository, https://github.com/bitranox/lib_detect_testenv.git
@@ -28,6 +28,7 @@ Requires-Dist: bandit<1.9,>=1.8.6; (python_version < '3.10') and extra == 'dev'
28
28
  Requires-Dist: bandit>=1.9.4; (python_version >= '3.10') and extra == 'dev'
29
29
  Requires-Dist: build>=1.6.1; extra == 'dev'
30
30
  Requires-Dist: click>=8.5.0; extra == 'dev'
31
+ Requires-Dist: hatchling>=1.32.4; extra == 'dev'
31
32
  Requires-Dist: import-linter<2.6,>=2.5.2; (python_version < '3.10') and extra == 'dev'
32
33
  Requires-Dist: import-linter>=2.15; (python_version >= '3.10') and extra == 'dev'
33
34
  Requires-Dist: pip-audit>=2.10.1; extra == 'dev'
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lib_detect_testenv"
3
- version = "3.0.5"
3
+ version = "3.0.6"
4
4
  description = "Detect test environment - pytest, doctest, unittest, or regular execution"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -40,6 +40,7 @@ dev = [
40
40
  "bandit>=1.8.6,<1.9; python_version<'3.10'",
41
41
  "bandit>=1.9.4; python_version>='3.10'",
42
42
  "build>=1.6.1",
43
+ "hatchling>=1.32.4",
43
44
  "twine>=7.0.0",
44
45
  # codecov-cli is disabled, not dropped: it requires click<8.3.0, which drags click back to
45
46
  # 8.2.1 - the version with CVE-2026-7246 (command injection in click.edit(), fixed in 8.3.3).
@@ -71,6 +72,21 @@ build-backend = "hatchling.build"
71
72
  packages = ["src/lib_detect_testenv"]
72
73
  include = ["src/lib_detect_testenv/py.typed"]
73
74
 
75
+ # An allowlist, never a denylist: hatchling reads only the root .gitignore, so tracked working
76
+ # files and files hidden through .git/info/exclude or a nested .gitignore (session records,
77
+ # review notes, helper scripts) would ship. tests/test_packaging.py builds the sdist and
78
+ # refuses any other top-level entry.
79
+ [tool.hatch.build.targets.sdist]
80
+ include = [
81
+ "/src/lib_detect_testenv",
82
+ "/tests/*.py",
83
+ "/docs",
84
+ "/README.md",
85
+ "/LICENSE",
86
+ "/CHANGELOG.md",
87
+ "/pyproject.toml",
88
+ ]
89
+
74
90
  [tool.ruff]
75
91
  line-length = 160
76
92
  target-version = "py39"
@@ -85,6 +101,7 @@ select = [
85
101
  "B", # bugbear
86
102
  "A", # builtins shadowing
87
103
  "C4", # comprehension simplification
104
+ "C90", # mccabe: cyclomatic complexity <= 10 (the house hard limit)
88
105
  "SIM", # simplification
89
106
  "TCH", # type-checking imports
90
107
  "RUF", # ruff-specific
@@ -26,7 +26,7 @@ name = "lib_detect_testenv"
26
26
  #: Human-readable summary shown in CLI help output.
27
27
  title = "Detect test environment - pytest, doctest, unittest, or regular execution"
28
28
  #: Current release version pulled from ``pyproject.toml`` by automation.
29
- version = "3.0.5"
29
+ version = "3.0.6"
30
30
  #: Repository homepage presented to users.
31
31
  homepage = "https://github.com/bitranox/lib_detect_testenv"
32
32
  #: Author attribution surfaced in CLI output.
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import re
6
+ import sys
6
7
  from typing import TYPE_CHECKING
7
8
 
8
9
  import pytest
@@ -48,3 +49,20 @@ def strip_ansi() -> Callable[[str], str]:
48
49
  return _remove_ansi_codes(value)
49
50
 
50
51
  return _strip
52
+
53
+
54
+ #: Which platforms each ``os_*`` marker lets a test run on. Registering a marker in pyproject only
55
+ #: silences pytest's unknown-marker warning; without this table a marked test runs everywhere.
56
+ _PLATFORM_MARKERS: dict[str, Callable[[str], bool]] = {
57
+ "os_windows": lambda platform: platform == "win32",
58
+ "os_macos": lambda platform: platform == "darwin",
59
+ "os_linux": lambda platform: platform.startswith("linux"),
60
+ "os_posix": lambda platform: platform != "win32",
61
+ }
62
+
63
+
64
+ def pytest_runtest_setup(item: pytest.Item) -> None:
65
+ """Skip a test whose ``os_*`` marker names a platform other than the running one."""
66
+ for name, runs_on in _PLATFORM_MARKERS.items():
67
+ if item.get_closest_marker(name) is not None and not runs_on(sys.platform):
68
+ pytest.skip(f"marked {name}; this is {sys.platform}")
@@ -0,0 +1,127 @@
1
+ """The source distribution carries the package, its tests and its docs, and nothing else.
2
+
3
+ A working tree holds files that must never be published: session records, backlogs, scratch
4
+ review notes and helper scripts. Some are tracked, some are hidden only through
5
+ ``.git/info/exclude`` or a nested ``.gitignore``, which hatchling does not read. The sdist is
6
+ therefore built from an allowlist, and this test builds one from a copy of the tree that also
7
+ holds such decoys. The wheel must still carry the package data the runtime loads.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import shutil
13
+ import tarfile
14
+ import zipfile
15
+ from pathlib import Path
16
+
17
+ import pytest
18
+ from hatchling.builders.sdist import SdistBuilder
19
+ from hatchling.builders.wheel import WheelBuilder
20
+
21
+ PROJECT_ROOT = Path(__file__).resolve().parent.parent
22
+ PACKAGE = "lib_detect_testenv"
23
+
24
+ # hatchling force-includes the root .gitignore into every sdist (so a build from the unpacked
25
+ # sdist applies the same exclusions); no include/exclude setting removes it.
26
+ ALLOWED_TOP_LEVEL = frozenset(
27
+ {
28
+ "PKG-INFO",
29
+ "pyproject.toml",
30
+ "README.md",
31
+ "LICENSE",
32
+ "CHANGELOG.md",
33
+ ".gitignore",
34
+ "src",
35
+ "tests",
36
+ "docs",
37
+ }
38
+ )
39
+ REQUIRED_TOP_LEVEL = frozenset({"PKG-INFO", "pyproject.toml", "src", "tests"})
40
+
41
+ # Files and directories that exist in working trees and must never ship. Tracked ones are
42
+ # named in the repository; the others are hidden locally through .git/info/exclude.
43
+ DECOY_FILES = ("handover.md", "OPEN-WORK.md", "EXECUTION-USER-REVIEW.md", "reset_git_history.sh", ".private/notes.txt")
44
+
45
+ REQUIRED_WHEEL_DATA = ("lib_detect_testenv/py.typed",)
46
+
47
+
48
+ def _copy_tree_with_decoys(destination: Path) -> Path:
49
+ """Copy the buildable tree and plant the files a working tree carries but must not ship."""
50
+ root = destination / "tree"
51
+ root.mkdir()
52
+ for name in ("pyproject.toml", "README.md", "LICENSE", "CHANGELOG.md", ".gitignore"):
53
+ shutil.copy2(PROJECT_ROOT / name, root / name)
54
+ for name in ("src", "docs", "tests"):
55
+ shutil.copytree(PROJECT_ROOT / name, root / name, ignore=shutil.ignore_patterns("__pycache__", "*.pyc"))
56
+ for decoy in DECOY_FILES:
57
+ path = root / decoy
58
+ path.parent.mkdir(parents=True, exist_ok=True)
59
+ path.write_text("decoy\n", encoding="utf-8")
60
+ return root
61
+
62
+
63
+ def _build_sdist(root: Path, directory: Path) -> Path:
64
+ built = list(SdistBuilder(str(root)).build(directory=str(directory), versions=["standard"]))
65
+ assert len(built) == 1, f"expected one sdist, got {built}"
66
+ return Path(built[0])
67
+
68
+
69
+ def _strip_root(names: list[str]) -> list[str]:
70
+ """Drop the ``<name>-<version>/`` prefix every member carries."""
71
+ return [name.split("/", 1)[1] for name in names if "/" in name]
72
+
73
+
74
+ @pytest.fixture(scope="module")
75
+ def built_tree(tmp_path_factory: pytest.TempPathFactory) -> Path:
76
+ return _copy_tree_with_decoys(tmp_path_factory.mktemp("packaging"))
77
+
78
+
79
+ @pytest.fixture(scope="module")
80
+ def sdist_members(built_tree: Path, tmp_path_factory: pytest.TempPathFactory) -> list[str]:
81
+ sdist = _build_sdist(built_tree, tmp_path_factory.mktemp("sdist"))
82
+ with tarfile.open(sdist, "r:gz") as archive:
83
+ members = _strip_root(archive.getnames())
84
+ assert members, "the sdist is empty, so nothing below would be judged"
85
+ return members
86
+
87
+
88
+ @pytest.fixture(scope="module")
89
+ def wheel_members(built_tree: Path, tmp_path_factory: pytest.TempPathFactory) -> list[str]:
90
+ built = list(WheelBuilder(str(built_tree)).build(directory=str(tmp_path_factory.mktemp("wheel")), versions=["standard"]))
91
+ assert len(built) == 1, f"expected one wheel, got {built}"
92
+ with zipfile.ZipFile(built[0]) as archive:
93
+ members = archive.namelist()
94
+ assert members, "the wheel is empty, so nothing below would be judged"
95
+ return members
96
+
97
+
98
+ @pytest.mark.os_agnostic
99
+ def test_the_sdist_holds_only_allowlisted_top_level_entries(sdist_members: list[str]) -> None:
100
+ top_level = {member.split("/", 1)[0] for member in sdist_members}
101
+
102
+ unexpected = sorted(top_level - ALLOWED_TOP_LEVEL)
103
+ missing = sorted(REQUIRED_TOP_LEVEL - top_level)
104
+
105
+ assert not unexpected, f"the sdist ships files outside the allowlist: {unexpected}"
106
+ assert not missing, f"the sdist lacks required entries: {missing}"
107
+
108
+
109
+ @pytest.mark.os_agnostic
110
+ def test_the_sdist_ships_only_the_package_under_src(sdist_members: list[str]) -> None:
111
+ under_src = {member.split("/")[1] for member in sdist_members if member.startswith("src/") and member.count("/") >= 1}
112
+
113
+ assert under_src == {PACKAGE}
114
+
115
+
116
+ @pytest.mark.os_agnostic
117
+ def test_the_sdist_carries_no_compiled_bytecode(sdist_members: list[str]) -> None:
118
+ compiled = [member for member in sdist_members if "__pycache__" in member or member.endswith(".pyc")]
119
+
120
+ assert not compiled, f"the sdist ships bytecode: {compiled[:5]}"
121
+
122
+
123
+ @pytest.mark.os_agnostic
124
+ def test_the_wheel_carries_the_package_data_the_runtime_loads(wheel_members: list[str]) -> None:
125
+ missing = [name for name in REQUIRED_WHEEL_DATA if name not in wheel_members]
126
+
127
+ assert not missing, f"the wheel lacks package data: {missing}"
@@ -0,0 +1,31 @@
1
+ """The ``os_*`` markers skip a test off its platform; registering them alone would skip nothing.
2
+
3
+ Each test asserts the platform its own marker names, so it fails wherever conftest lets it run by
4
+ mistake, and passes where the marker lets it run.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+
11
+ import pytest
12
+
13
+
14
+ @pytest.mark.os_windows
15
+ def test_an_os_windows_test_runs_only_on_windows() -> None:
16
+ assert sys.platform == "win32"
17
+
18
+
19
+ @pytest.mark.os_macos
20
+ def test_an_os_macos_test_runs_only_on_macos() -> None:
21
+ assert sys.platform == "darwin"
22
+
23
+
24
+ @pytest.mark.os_linux
25
+ def test_an_os_linux_test_runs_only_on_linux() -> None:
26
+ assert sys.platform.startswith("linux")
27
+
28
+
29
+ @pytest.mark.os_posix
30
+ def test_an_os_posix_test_never_runs_on_windows() -> None:
31
+ assert sys.platform != "win32"
@@ -1,22 +0,0 @@
1
- {
2
- "image": "mcr.microsoft.com/devcontainers/python:3.13",
3
-
4
- "postCreateCommand": "bash -lc '\nPY=/usr/local/bin/python;\n$PY -m pip install -U pip && \\\n$PY -m pip install ipykernel && \\\n$PY -m pip install -e . && \\\n# register kernel using this exact interpreter\n$PY -m ipykernel install --name=python3 --display-name=\"Python 3 (3.13)\" --user && \\\n# patch the notebook to point to the python3 kernelspec (VS Code will bind to /usr/local/bin/python)\n$PY - <<\"PY\"\nimport json, pathlib\np = pathlib.Path(\"notebooks/Quickstart.ipynb\")\nif p.exists():\n nb = json.loads(p.read_text(encoding=\"utf-8\"))\n md = nb.setdefault(\"metadata\", {})\n md[\"kernelspec\"] = {\n \"name\": \"python3\",\n \"display_name\": \"Python 3 (3.13)\",\n \"language\": \"python\"\n }\n md[\"language_info\"] = {\"name\": \"python\"}\n p.write_text(json.dumps(nb, ensure_ascii=False, indent=1), encoding=\"utf-8\")\n print(\"Pinned kernelspec to python3 in\", p)\nelse:\n print(\"Notebook not found:\", p)\nPY'\n",
5
-
6
- "customizations": {
7
- "vscode": {
8
- "extensions": ["ms-toolsai.jupyter", "ms-python.python"],
9
- "settings": {
10
- "workbench.startupEditor": "none",
11
- "jupyter.alwaysTrustNotebooks": true,
12
- "jupyter.kernelPickerType": "OnlyRecommended",
13
- "python.defaultInterpreterPath": "/usr/local/bin/python"
14
- }
15
- },
16
- "codespaces": {
17
- "openFiles": ["README.md"]
18
- }
19
- },
20
-
21
- "postAttachCommand": "bash -lc 'if [ -f notebooks/Quickstart.ipynb ]; then code -r notebooks/Quickstart.ipynb; fi'"
22
- }
@@ -1,6 +0,0 @@
1
- {
2
- "python.defaultInterpreterPath": "/usr/local/bin/python3.13",
3
- "jupyter.alwaysTrustNotebooks": true,
4
- "jupyter.kernelPickerType": "OnlyRecommended",
5
- "jupyter.askForKernelRestart": false
6
- }
@@ -1,11 +0,0 @@
1
- # Copy to .env and fill values as needed.
2
-
3
- # Codecov upload token (required for private repos; optional for public repos)
4
- CODECOV_TOKEN=
5
-
6
- # PyPI API token for release workflow (.github/workflows/release.yml)
7
- # Format: pypi-AgENdGVzdC5weXBpLm9yZwIk...
8
- PYPI_API_TOKEN=
9
-
10
- # Optional: GitHub token for API-limited tasks
11
- GITHUB_TOKEN=
@@ -1,108 +0,0 @@
1
- name: Extract project metadata
2
- description: >-
3
- Parse pyproject.toml and expose project metadata as step outputs.
4
- Requires Python to be available in the runner environment.
5
- Auto-installs rtoml if not already present.
6
-
7
- outputs:
8
- PACKAGE_MODULE:
9
- description: Python-importable module name (underscored)
10
- value: ${{ steps.meta.outputs.PACKAGE_MODULE }}
11
- PROJECT_NAME:
12
- description: Project name from pyproject.toml
13
- value: ${{ steps.meta.outputs.PROJECT_NAME }}
14
- CLI_BIN:
15
- description: CLI entry-point binary name
16
- value: ${{ steps.meta.outputs.CLI_BIN }}
17
- SRC_PATH:
18
- description: Source directory path (e.g. "src")
19
- value: ${{ steps.meta.outputs.SRC_PATH }}
20
- COV_FAIL_UNDER:
21
- description: Minimum coverage percentage from tool.coverage.report.fail_under
22
- value: ${{ steps.meta.outputs.COV_FAIL_UNDER }}
23
- COV_REPORT_FILE:
24
- description: Coverage report filename (e.g. "coverage.xml")
25
- value: ${{ steps.meta.outputs.COV_REPORT_FILE }}
26
- PYTEST_VERBOSITY:
27
- description: Pytest verbosity flag (e.g. "-vv")
28
- value: ${{ steps.meta.outputs.PYTEST_VERBOSITY }}
29
- BANDIT_SKIP_FLAG:
30
- description: Bandit skip flag string (e.g. "-s B101,B601") or empty
31
- value: ${{ steps.meta.outputs.BANDIT_SKIP_FLAG }}
32
- PYTEST_MARKER_EXPR:
33
- description: >-
34
- Marker expression for the CI test run (e.g. "not local_only and not integration"),
35
- built from tool.ci.pytest-exclude-markers
36
- value: ${{ steps.meta.outputs.PYTEST_MARKER_EXPR }}
37
-
38
- runs:
39
- using: composite
40
- steps:
41
- - name: Extract project metadata from pyproject.toml
42
- id: meta
43
- shell: python
44
- run: |
45
- import os, subprocess, sys
46
- try:
47
- import rtoml
48
- except ModuleNotFoundError:
49
- subprocess.check_call([sys.executable, "-m", "pip", "install", "rtoml", "-q"])
50
- import rtoml
51
-
52
- from pathlib import Path
53
- data = rtoml.load(Path('pyproject.toml'))
54
-
55
- # --- [project] section ---
56
- project = data['project']['name']
57
- module = project.replace('-', '_')
58
- dash = project.replace('_', '-')
59
- scripts = list(data['project'].get('scripts', {}).keys())
60
- cli_bin = scripts[0] if scripts else dash
61
-
62
- # --- [tool.scripts.test] section ---
63
- scripts_test = data.get('tool', {}).get('scripts', {}).get('test', {})
64
- src_path = scripts_test.get('src-path', 'src')
65
- pytest_verbosity = scripts_test.get('pytest-verbosity', '-vv')
66
- cov_report_file = scripts_test.get('coverage-report-file', 'coverage.xml')
67
-
68
- # --- [tool.coverage.report] section ---
69
- cov_fail_under = (
70
- data.get('tool', {}).get('coverage', {}).get('report', {}).get('fail_under', 90)
71
- )
72
-
73
- # --- [tool.bandit] section ---
74
- bandit_skips = data.get('tool', {}).get('bandit', {}).get('skips', [])
75
- bandit_skip_flag = f"-s {','.join(bandit_skips)}" if bandit_skips else ""
76
-
77
- # --- [tool.ci] section ---
78
- # CI always skips local_only (those tests need a dev box). A project whose
79
- # suite also carries markers CI cannot satisfy - integration tests needing a
80
- # container that a separate workflow starts - names them here, rather than
81
- # every such repo forking this workflow to edit one -m expression.
82
- ci_config = data.get('tool', {}).get('ci', {})
83
- exclude_markers = ['local_only', *ci_config.get('pytest-exclude-markers', [])]
84
- pytest_marker_expr = ' and '.join(f'not {m}' for m in exclude_markers)
85
-
86
- # Write to GITHUB_OUTPUT for composite action outputs
87
- with open(os.environ['GITHUB_OUTPUT'], 'a', encoding='utf-8') as out:
88
- out.write(f"PROJECT_NAME={project}\n")
89
- out.write(f"PACKAGE_MODULE={module}\n")
90
- out.write(f"CLI_BIN={cli_bin}\n")
91
- out.write(f"SRC_PATH={src_path}\n")
92
- out.write(f"PYTEST_VERBOSITY={pytest_verbosity}\n")
93
- out.write(f"COV_REPORT_FILE={cov_report_file}\n")
94
- out.write(f"COV_FAIL_UNDER={cov_fail_under}\n")
95
- out.write(f"BANDIT_SKIP_FLAG={bandit_skip_flag}\n")
96
- out.write(f"PYTEST_MARKER_EXPR={pytest_marker_expr}\n")
97
-
98
- # Also write to GITHUB_ENV so subsequent steps can use ${{ env.VAR }}
99
- with open(os.environ['GITHUB_ENV'], 'a', encoding='utf-8') as env:
100
- env.write(f"PROJECT_NAME={project}\n")
101
- env.write(f"PACKAGE_MODULE={module}\n")
102
- env.write(f"CLI_BIN={cli_bin}\n")
103
- env.write(f"SRC_PATH={src_path}\n")
104
- env.write(f"PYTEST_VERBOSITY={pytest_verbosity}\n")
105
- env.write(f"COV_REPORT_FILE={cov_report_file}\n")
106
- env.write(f"COV_FAIL_UNDER={cov_fail_under}\n")
107
- env.write(f"BANDIT_SKIP_FLAG={bandit_skip_flag}\n")
108
- env.write(f"PYTEST_MARKER_EXPR={pytest_marker_expr}\n")
@@ -1,22 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: "pip"
4
- directory: "/"
5
- schedule:
6
- interval: "weekly"
7
- allow:
8
- - dependency-type: "direct"
9
- labels:
10
- - "dependencies"
11
- commit-message:
12
- prefix: "deps"
13
- include: "scope"
14
- - package-ecosystem: "github-actions"
15
- directory: "/"
16
- schedule:
17
- interval: "weekly"
18
- labels:
19
- - "dependencies"
20
- commit-message:
21
- prefix: "deps"
22
- include: "scope"
@@ -1,39 +0,0 @@
1
- name: CodeQL
2
-
3
- on:
4
- push:
5
- branches: [ main, master ]
6
- pull_request:
7
- branches: [ main, master ]
8
- schedule:
9
- - cron: '0 8 * * 1'
10
-
11
- jobs:
12
- analyze:
13
- name: CodeQL Analyze
14
- runs-on: ubuntu-latest
15
- permissions:
16
- actions: read
17
- contents: read
18
- security-events: write
19
- strategy:
20
- fail-fast: false
21
- matrix:
22
- language: [ 'python' ]
23
-
24
- steps:
25
- - name: Checkout repository
26
- uses: actions/checkout@v7
27
-
28
- - name: Initialize CodeQL
29
- uses: github/codeql-action/init@v4
30
- with:
31
- languages: ${{ matrix.language }}
32
-
33
- - name: Autobuild
34
- uses: github/codeql-action/autobuild@v4
35
-
36
- - name: Perform CodeQL Analysis
37
- uses: github/codeql-action/analyze@v4
38
- with:
39
- category: "/language:${{ matrix.language }}"