Public Access
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4dda91e24 | ||
|
|
3e21e774f7 | ||
|
|
7c11215e57 | ||
|
|
5384269c83 | ||
|
|
b3d0dd8ca7 | ||
|
|
a7dcaee5c6 | ||
|
|
02f8d3757b |
@@ -27,7 +27,7 @@ jobs:
|
||||
PYTHONPATH: src
|
||||
run: |
|
||||
. .venv/bin/activate
|
||||
python3 -m devx.tools.check_test_speed --max-seconds 60 --max-single-seconds 2.0
|
||||
python3 -m devx.tools.check_test_speed --max-seconds 4 --max-single-seconds 0.5
|
||||
- name: Documentation coverage check
|
||||
env:
|
||||
PYTHONPATH: src
|
||||
|
||||
@@ -2,6 +2,25 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.8.3] - 2026-06-23
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Lower check_test_speed threshold to 4 seconds
|
||||
- Pass --break-system-packages to pip in CI environments
|
||||
|
||||
## [0.8.2] - 2026-06-23
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Encode spaces in pair commands to survive shell word-splitting
|
||||
|
||||
## [0.8.1] - 2026-06-23
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Set fresh MOLECULE_HOME per pair to avoid stale config cache
|
||||
|
||||
## [0.8.0] - 2026-06-23
|
||||
|
||||
### Features
|
||||
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
# Aligned with CI (ci.yml uses same thresholds).
|
||||
set -e
|
||||
export PYTHONPATH=src
|
||||
python3 -m devx.tools.check_test_speed --max-seconds 10 --max-single-seconds 0.5
|
||||
python3 -m devx.tools.check_test_speed --max-seconds 4 --max-single-seconds 0.5
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""devx — reusable development and CI/CD tools for oblachno-oss projects."""
|
||||
|
||||
__version__ = "0.8.0"
|
||||
__version__ = "0.8.3"
|
||||
|
||||
@@ -41,7 +41,8 @@ class TestPair:
|
||||
|
||||
def encode(self) -> str:
|
||||
"""Serialize to a pipe-delimited string for CI consumption."""
|
||||
return f"{self.scenario}|{self.platform['name']}|{self.platform['image']}|{self.platform['command']}"
|
||||
cmd = self.platform["command"].replace(" ", "__SPACE__")
|
||||
return f"{self.scenario}|{self.platform['name']}|{self.platform['image']}|{cmd}"
|
||||
|
||||
@staticmethod
|
||||
def decode(encoded: str) -> TestPair:
|
||||
@@ -49,7 +50,7 @@ class TestPair:
|
||||
parts = encoded.split("|")
|
||||
return TestPair(
|
||||
scenario=parts[0],
|
||||
platform={"name": parts[1], "image": parts[2], "command": parts[3]},
|
||||
platform={"name": parts[1], "image": parts[2], "command": parts[3].replace("__SPACE__", " ")},
|
||||
)
|
||||
|
||||
|
||||
@@ -63,9 +64,8 @@ class MultiRoleTestPair:
|
||||
|
||||
def encode(self) -> str:
|
||||
"""Serialize to a pipe-delimited string: ``role|scenario|platform_name|image|command``."""
|
||||
return (
|
||||
f"{self.role}|{self.scenario}|{self.platform['name']}|{self.platform['image']}|{self.platform['command']}"
|
||||
)
|
||||
cmd = self.platform["command"].replace(" ", "__SPACE__")
|
||||
return f"{self.role}|{self.scenario}|{self.platform['name']}|{self.platform['image']}|{cmd}"
|
||||
|
||||
@staticmethod
|
||||
def decode(encoded: str) -> MultiRoleTestPair:
|
||||
@@ -74,7 +74,7 @@ class MultiRoleTestPair:
|
||||
return MultiRoleTestPair(
|
||||
role=parts[0],
|
||||
scenario=parts[1],
|
||||
platform={"name": parts[2], "image": parts[3], "command": parts[4]},
|
||||
platform={"name": parts[2], "image": parts[3], "command": parts[4].replace("__SPACE__", " ")},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -114,12 +114,14 @@ def parse_pair(pair: str) -> tuple[str, str, str, str, str]:
|
||||
|
||||
Supports both 4-part (single-role) and 5-part (multi-role) formats.
|
||||
For 4-part pairs, role is empty (caller uses default role dir).
|
||||
Spaces in the command field are encoded as ``__SPACE__`` to survive
|
||||
shell word-splitting when ``$TEST_PAIRS`` is expanded unquoted.
|
||||
"""
|
||||
parts = pair.split("|")
|
||||
if len(parts) == 4:
|
||||
return "", parts[0], parts[1], parts[2], parts[3]
|
||||
return "", parts[0], parts[1], parts[2], parts[3].replace("__SPACE__", " ")
|
||||
if len(parts) == 5:
|
||||
return parts[0], parts[1], parts[2], parts[3], parts[4]
|
||||
return parts[0], parts[1], parts[2], parts[3], parts[4].replace("__SPACE__", " ")
|
||||
raise click.ClickException(f"Invalid pair format: {pair!r} (expected 4 or 5 pipe-delimited parts)")
|
||||
|
||||
|
||||
@@ -134,6 +136,12 @@ def build_env_for_pair(pair: str, base_env: dict[str, str]) -> dict[str, str]:
|
||||
elif "MOLECULE_PLATFORM_COMMAND" in env:
|
||||
del env["MOLECULE_PLATFORM_COMMAND"]
|
||||
env["ANSIBLE_ALLOW_BROKEN_CONDITIONALS"] = "true"
|
||||
# Use a fresh MOLECULE_HOME per pair to avoid stale config cache
|
||||
# from previous CI runs (causes "Instances missing" errors).
|
||||
if "MOLECULE_HOME" not in env:
|
||||
import tempfile
|
||||
|
||||
env["MOLECULE_HOME"] = tempfile.mkdtemp(prefix="molecule-ci-")
|
||||
return env
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,12 @@ def _run(cmd: list[str]) -> None:
|
||||
def _install_python_deps(bin_dir: str, extras: str = "dev") -> None:
|
||||
"""Install the project with the specified extras in editable mode."""
|
||||
pip = str(Path(bin_dir) / "pip")
|
||||
_run([pip, "install", "-e", f".[{extras}]"])
|
||||
cmd = [pip, "install", "-e", f".[{extras}]"]
|
||||
# In CI (system Python), --break-system-packages allows upgrading
|
||||
# debian-installed packages (e.g. platformdirs) that lack RECORD files.
|
||||
if os.environ.get("PIP_BREAK_SYSTEM_PACKAGES") == "1":
|
||||
cmd.append("--break-system-packages")
|
||||
_run(cmd)
|
||||
|
||||
|
||||
def _install_pre_commit_hooks(bin_dir: str) -> None:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Unit tests for devx.tools.setup."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
@@ -47,6 +48,12 @@ class TestInstallPythonDeps:
|
||||
_install_python_deps(".venv/bin", "ci,lint")
|
||||
mock_run.assert_called_once_with([".venv/bin/pip", "install", "-e", ".[ci,lint]"])
|
||||
|
||||
@patch("devx.tools.setup._run")
|
||||
def test_install_with_break_system_packages(self, mock_run: MagicMock) -> None:
|
||||
with patch.dict(os.environ, {"PIP_BREAK_SYSTEM_PACKAGES": "1"}):
|
||||
_install_python_deps(".venv/bin", "ci")
|
||||
mock_run.assert_called_once_with([".venv/bin/pip", "install", "-e", ".[ci]", "--break-system-packages"])
|
||||
|
||||
|
||||
class TestInstallPreCommitHooks:
|
||||
@patch("devx.tools.setup._run")
|
||||
|
||||
Reference in New Issue
Block a user