refactor: remove deprecated devx.ci.discover_runners wrapper

All workflows now use devx.molecule.discover_runners directly. The
deprecated wrapper (added in Phase 1c for backward compatibility) is
no longer referenced by any workflow in devx, grm, or infra.

- Delete src/devx/ci/discover_runners.py
- Remove `devx ci discover-runners` CLI subcommand
- Update tests to import from devx.molecule.discover_runners
- Remove TestDeprecationWrapper class
- Update docs, README, AGENTS.md, ADR-0002

DEVX-160

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
emil
2026-08-13 01:35:51 +02:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 10de782d93
commit 2652352205
11 changed files with 21 additions and 109 deletions
+1 -2
View File
@@ -167,7 +167,6 @@ src/devx/
│ ├── cancel_superseded_runs.py # Cancel in-flight CI runs for the same PR branch │ ├── cancel_superseded_runs.py # Cancel in-flight CI runs for the same PR branch
│ ├── check_workflow_artifact_deps.py # Verify artifact download jobs depend on upload jobs │ ├── check_workflow_artifact_deps.py # Verify artifact download jobs depend on upload jobs
│ ├── check_workflow_tofu_init.py # Verify tofu-state jobs have a tofu-init step │ ├── check_workflow_tofu_init.py # Verify tofu-state jobs have a tofu-init step
│ ├── discover_runners.py # Deprecated wrapper → molecule/discover_runners
│ └── wait_for_checks.py # Poll Gitea Actions for job completion (replaces inline shell polling) │ └── wait_for_checks.py # Poll Gitea Actions for job completion (replaces inline shell polling)
├── tools/ # Developer tooling modules (run locally or by CI) ├── tools/ # Developer tooling modules (run locally or by CI)
│ ├── setup.py # Environment setup (venv, deps, hooks) │ ├── setup.py # Environment setup (venv, deps, hooks)
@@ -220,7 +219,7 @@ src/devx/
│ ├── ui.py # say() — unified click.echo + logging output │ ├── ui.py # say() — unified click.echo + logging output
│ └── jinja.py # Jinja2 environment helpers + Ansible-compatible filters │ └── jinja.py # Jinja2 environment helpers + Ansible-compatible filters
└── molecule/ # Optional molecule testing helpers (for Ansible projects) └── molecule/ # Optional molecule testing helpers (for Ansible projects)
├── discover_runners.py # Dynamic Gitea runner discovery (canonical; ci/discover_runners is a deprecated wrapper) ├── discover_runners.py # Dynamic Gitea runner discovery
├── distribute_molecule.py # Distribute molecule scenarios across runners (LPT scheduling, --roles-root for multi-role) ├── distribute_molecule.py # Distribute molecule scenarios across runners (LPT scheduling, --roles-root for multi-role)
├── molecule_ci_guard.py # Run molecule with cross-runner fail-fast (--roles-root) ├── molecule_ci_guard.py # Run molecule with cross-runner fail-fast (--roles-root)
├── molecule_all.py # Run all molecule scenarios locally ├── molecule_all.py # Run all molecule scenarios locally
+8
View File
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [Unreleased]
### Breaking Changes
- Remove deprecated `devx.ci.discover_runners` wrapper. All workflows
now use `devx.molecule.discover_runners` directly. The `devx ci
discover-runners` CLI subcommand has also been removed.
## [0.50.6] - 2026-08-12 ## [0.50.6] - 2026-08-12
### Bug Fixes ### Bug Fixes
+1 -1
View File
@@ -172,7 +172,7 @@ python -m devx.ci.notify_failure --repo oblachno-oss/devx --run-id 123 \
--workflow ci --commit abc123 --auto-login --workflow ci --commit abc123 --auto-login
# Discover available Gitea Actions runners # Discover available Gitea Actions runners
python -m devx.ci.discover_runners --owner oblachno-oss --repo devx --indices python -m devx.molecule.discover_runners --owner oblachno-oss --repo devx --indices
# Distribute files across parallel runners (round-robin) # Distribute files across parallel runners (round-robin)
python -m devx.ci.distribute_files --pattern "tests/integration/test_*.py" \ python -m devx.ci.distribute_files --pattern "tests/integration/test_*.py" \
@@ -81,17 +81,13 @@ Extract the inline polling logic into `devx.ci.wait_for_checks`:
This replaces the inline shell polling in `grm` `ci.yml` with a This replaces the inline shell polling in `grm` `ci.yml` with a
reusable, testable Python module. reusable, testable Python module.
### 3. Deprecated `ci/discover_runners` Wrapper ### 3. Removed `ci/discover_runners` Wrapper
Merge the `ci/discover_runners` implementation (with its better error Merged the `ci/discover_runners` implementation (with its better error
logging) into `molecule/discover_runners` as the canonical version. logging) into `molecule/discover_runners` as the canonical version.
Make `ci/discover_runners` a deprecated wrapper that: The `ci/discover_runners` wrapper was deprecated in Phase 1c and
**removed in Phase 2d** (DEVX-160). All workflows now use
- Re-exports all public symbols from `molecule.discover_runners` `devx.molecule.discover_runners` directly.
- Emits a `DeprecationWarning` when run as `__main__`
- Preserves backward compatibility for existing workflow references
New code should import from `devx.molecule.discover_runners` directly.
## Consequences ## Consequences
+1 -1
View File
@@ -409,7 +409,7 @@ Intended for local development; CI uses the parallel matrix instead.
### `molecule/discover_runners.py` ### `molecule/discover_runners.py`
Discovers available Gitea Actions runners for molecule tests. This is the Discovers available Gitea Actions runners for molecule tests. This is the
canonical implementation; `devx.ci.discover_runners` is a deprecated wrapper canonical implementation (formerly `devx.ci.discover_runners`, removed in v0.51.0)
that re-exports from this module. Queries runners at repository, that re-exports from this module. Queries runners at repository,
organization, and instance (administrator) levels, with warnings logged organization, and instance (administrator) levels, with warnings logged
to stderr on non-200 responses (except 403 on instance-level, which is to stderr on non-200 responses (except 403 on instance-level, which is
+1 -1
View File
@@ -456,7 +456,7 @@ instance levels. Falls back to `MOLECULE_RUNNERS` repo variable or
`DEFAULT_MAX_RUNNERS` (3). `DEFAULT_MAX_RUNNERS` (3).
```bash ```bash
python -m devx.ci.discover_runners --owner <owner> --repo <repo> [--count] [--indices] python -m devx.molecule.discover_runners --owner <owner> --repo <repo> [--count] [--indices]
``` ```
### `detect_release_commit.py` ### `detect_release_commit.py`
+1 -1
View File
@@ -142,7 +142,7 @@ infrastructure = [
- `devx.ci.notify_failure` — Create Gitea issues on CI failures - `devx.ci.notify_failure` — Create Gitea issues on CI failures
- `devx.ci.distribute_files` — Parallel test file distribution - `devx.ci.distribute_files` — Parallel test file distribution
- `devx.ci.distribute_items` — Parallel item distribution across runners - `devx.ci.distribute_items` — Parallel item distribution across runners
- `devx.ci.discover_runners` — Dynamic runner discovery via Gitea API - `devx.molecule.discover_runners` — Dynamic runner discovery via Gitea API
### Development Tools (`devx.tools.*`) ### Development Tools (`devx.tools.*`)
-49
View File
@@ -1,49 +0,0 @@
#!/usr/bin/env python3
"""Discover available Gitea Actions runners for dynamic job distribution.
.. deprecated:: Phase 1c
Use :mod:`devx.molecule.discover_runners` instead. This module is a
thin wrapper that re-exports the canonical implementation from
:mod:`devx.molecule.discover_runners` for backward compatibility
with existing workflow references and Makefile targets.
The canonical implementation lives in
:mod:`devx.molecule.discover_runners` because runner discovery is
primarily used by the molecule test distribution pipeline. CI
workflows that still reference ``python -m devx.ci.discover_runners``
will continue to work via this wrapper, but new code should import
from :mod:`devx.molecule.discover_runners` directly.
Usage:
python3 -m devx.ci.discover_runners --owner oblachno-oss --repo devx
python3 -m devx.ci.discover_runners --indices
python3 -m devx.ci.discover_runners --count
"""
from __future__ import annotations
import sys
import warnings
from devx.molecule.discover_runners import ( # noqa: F401 — re-exported for backward compat
DEFAULT_MAX_RUNNERS,
generate_indices,
get_runner_count,
main,
query_runners,
)
_DEPRECATION_MSG = (
"devx.ci.discover_runners is deprecated; use devx.molecule.discover_runners instead. "
"This wrapper will be removed in a future release."
)
def _emit_deprecation_warning() -> None:
"""Emit a DeprecationWarning when this module is imported for CLI use."""
warnings.warn(_DEPRECATION_MSG, DeprecationWarning, stacklevel=2)
if __name__ == "__main__": # pragma: no cover
_emit_deprecation_warning()
sys.exit(main())
-7
View File
@@ -81,13 +81,6 @@ def ci_detect_release_commit(args: tuple[str, ...]) -> None:
_run_module("devx.ci.detect_release_commit", list(args)) _run_module("devx.ci.detect_release_commit", list(args))
@ci.command("discover-runners")
@click.argument("args", nargs=-1)
def ci_discover_runners(args: tuple[str, ...]) -> None:
"""Discover available Gitea Actions runners."""
_run_module("devx.ci.discover_runners", list(args))
@ci.command("doc-coverage") @ci.command("doc-coverage")
@click.argument("args", nargs=-1) @click.argument("args", nargs=-1)
def ci_doc_coverage(args: tuple[str, ...]) -> None: def ci_doc_coverage(args: tuple[str, ...]) -> None:
-7
View File
@@ -73,13 +73,6 @@ class TestCiCommands:
assert result.exit_code == 0 assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.detect_release_commit", []) mock_run.assert_called_once_with("devx.ci.detect_release_commit", [])
@patch("devx.cli._run_module")
def test_ci_discover_runners(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "discover-runners", "positional"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.discover_runners", ["positional"])
@patch("devx.cli._run_module") @patch("devx.cli._run_module")
def test_ci_doc_coverage(self, mock_run: MagicMock) -> None: def test_ci_doc_coverage(self, mock_run: MagicMock) -> None:
runner = CliRunner() runner = CliRunner()
+3 -31
View File
@@ -1,8 +1,6 @@
"""Unit tests for devx.ci.discover_runners (deprecated wrapper). """Unit tests for devx.molecule.discover_runners.
The wrapper re-exports from devx.molecule.discover_runners; these tests Tests verify the canonical implementation by patching the requests module.
verify backward compatibility by importing through the wrapper and
patching the canonical implementation's requests module.
""" """
import json import json
@@ -13,7 +11,7 @@ import click
import pytest import pytest
from click.testing import CliRunner from click.testing import CliRunner
from devx.ci.discover_runners import ( from devx.molecule.discover_runners import (
DEFAULT_MAX_RUNNERS, DEFAULT_MAX_RUNNERS,
generate_indices, generate_indices,
get_runner_count, get_runner_count,
@@ -266,29 +264,3 @@ class TestMain:
assert result.output.strip() == "3" assert result.output.strip() == "3"
args, _ = mock_count.call_args args, _ = mock_count.call_args
assert args[1] is None # token passed as None when missing assert args[1] is None # token passed as None when missing
class TestDeprecationWrapper:
def test_re_exports_canonical_symbols(self) -> None:
"""The wrapper re-exports the canonical implementation's symbols."""
from devx.ci import discover_runners as ci_mod
from devx.molecule import discover_runners as mol_mod
assert ci_mod.query_runners is mol_mod.query_runners
assert ci_mod.get_runner_count is mol_mod.get_runner_count
assert ci_mod.generate_indices is mol_mod.generate_indices
assert ci_mod.main is mol_mod.main
assert ci_mod.DEFAULT_MAX_RUNNERS is mol_mod.DEFAULT_MAX_RUNNERS
def test_emit_deprecation_warning(self) -> None:
"""_emit_deprecation_warning issues a DeprecationWarning."""
import warnings
from devx.ci.discover_runners import _emit_deprecation_warning
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
_emit_deprecation_warning()
assert len(caught) == 1
assert issubclass(caught[0].category, DeprecationWarning)
assert "deprecated" in str(caught[0].message)