DEVX-117: refactor: remove project-specific references from devx
This commit is contained in:
@@ -572,7 +572,7 @@ the user should not need to specify which profile to use.
|
|||||||
|
|
||||||
### Available Profiles
|
### Available Profiles
|
||||||
|
|
||||||
**Global** (shared with infra and grm):
|
**Global** (shared across all projects):
|
||||||
|
|
||||||
| Profile | Location | Purpose |
|
| Profile | Location | Purpose |
|
||||||
|---------|----------|---------|
|
|---------|----------|---------|
|
||||||
|
|||||||
@@ -185,7 +185,6 @@ def main(translations: tuple[Path, ...], source_dir: str | None) -> None:
|
|||||||
# Try common locations
|
# Try common locations
|
||||||
candidates = [
|
candidates = [
|
||||||
root / "src" / "devx" / "translations.json",
|
root / "src" / "devx" / "translations.json",
|
||||||
root / "src" / "gitea_runner_manager" / "translations.json",
|
|
||||||
]
|
]
|
||||||
# Also search for any translations.json in src/
|
# Also search for any translations.json in src/
|
||||||
for match in root.glob("src/*/translations.json"):
|
for match in root.glob("src/*/translations.json"):
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ ordering, then assigned to *max_runners* groups using LPT (Longest
|
|||||||
Processing Time first) scheduling.
|
Processing Time first) scheduling.
|
||||||
|
|
||||||
Each item is a string (e.g. an Ansible ``--limit`` pattern like
|
Each item is a string (e.g. an Ansible ``--limit`` pattern like
|
||||||
``observability`` or ``infra-314-vm``). Optionally, items can be objects
|
``observability`` or ``customer-1-vm``). Optionally, items can be objects
|
||||||
with ``{"id": "...", "weight": N}`` to provide explicit weights.
|
with ``{"id": "...", "weight": N}`` to provide explicit weights.
|
||||||
|
|
||||||
The assigned group for *runner_index* is written to ``$GITHUB_ENV`` as
|
The assigned group for *runner_index* is written to ``$GITHUB_ENV`` as
|
||||||
@@ -15,7 +15,7 @@ The assigned group for *runner_index* is written to ``$GITHUB_ENV`` as
|
|||||||
|
|
||||||
Usage::
|
Usage::
|
||||||
|
|
||||||
echo '["observability", "infra-314-vm"]' | \\
|
echo '["observability", "customer-1-vm"]' | \\
|
||||||
python3 -m devx.ci.distribute_items \\
|
python3 -m devx.ci.distribute_items \\
|
||||||
--runner-index 1 --max-runners 3 \\
|
--runner-index 1 --max-runners 3 \\
|
||||||
--github-env --skip-if-excess
|
--github-env --skip-if-excess
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ DEVX_PIP_INSTALL := if [ -z "$$CI_GITEA_TOKEN" ]; then . ./.env 2>/dev/null; fi;
|
|||||||
# ── Virtual environment management ────────────────────────────────────────────
|
# ── Virtual environment management ────────────────────────────────────────────
|
||||||
#
|
#
|
||||||
# These targets provide a single, consistent venv setup across all
|
# These targets provide a single, consistent venv setup across all
|
||||||
# devx-integrated projects (infra, grm, devx). Each project includes
|
# devx-integrated projects. Each project includes
|
||||||
# devx.mak and aliases its local targets to these.
|
# devx.mak and aliases its local targets to these.
|
||||||
#
|
#
|
||||||
# The venv is a standard .venv directory (no pyenv virtualenv dependency).
|
# The venv is a standard .venv directory (no pyenv virtualenv dependency).
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Outputs:
|
|||||||
- (default): prints both as ``count=N`` and ``indices=[0,1,...]``
|
- (default): prints both as ``count=N`` and ``indices=[0,1,...]``
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
python3 -m devx.molecule.discover_runners --owner oblachno-oss --repo grm
|
python3 -m devx.molecule.discover_runners --owner my-org --repo my-repo
|
||||||
python3 -m devx.molecule.discover_runners --indices
|
python3 -m devx.molecule.discover_runners --indices
|
||||||
python3 -m devx.molecule.discover_runners --count
|
python3 -m devx.molecule.discover_runners --count
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ def build_multi_role_pairs(
|
|||||||
# --- Molecule weight configuration ---
|
# --- Molecule weight configuration ---
|
||||||
#
|
#
|
||||||
# Weights are loaded from ``[tool.devx.molecule.weights]`` in
|
# Weights are loaded from ``[tool.devx.molecule.weights]`` in
|
||||||
# ``pyproject.toml``. Each project (infra, grm, …) contributes its own
|
# ``pyproject.toml``. Each project contributes its own
|
||||||
# weights calibrated from actual CI execution times.
|
# weights calibrated from actual CI execution times.
|
||||||
#
|
#
|
||||||
# Two key formats are supported:
|
# Two key formats are supported:
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ exits early with code 1.
|
|||||||
|
|
||||||
Usage::
|
Usage::
|
||||||
|
|
||||||
# Single-role (grm-style)
|
# Single-role
|
||||||
python3 -m devx.molecule.molecule_ci_guard pair1 pair2 ...
|
python3 -m devx.molecule.molecule_ci_guard pair1 pair2 ...
|
||||||
# Multi-role (infra-style)
|
# Multi-role
|
||||||
python3 -m devx.molecule.molecule_ci_guard --roles-root ansible/roles pair1 pair2 ...
|
python3 -m devx.molecule.molecule_ci_guard --roles-root ansible/roles pair1 pair2 ...
|
||||||
|
|
||||||
Environment variables:
|
Environment variables:
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ def main(repo: str | None, owner: str | None, branch: str, api_url: str | None)
|
|||||||
if not repo:
|
if not repo:
|
||||||
raise click.ClickException(_("ERROR: Repository name not specified. Use --repo or set DEVX_REPO_NAME."))
|
raise click.ClickException(_("ERROR: Repository name not specified. Use --repo or set DEVX_REPO_NAME."))
|
||||||
|
|
||||||
# If DEVX_REPO_NAME contains a slash (e.g. "oblachno/infra"), split into owner/repo.
|
# If DEVX_REPO_NAME contains a slash (e.g. "my-org/my-repo"), split into owner/repo.
|
||||||
# This prevents 404s when workflows set DEVX_REPO_NAME to the full path.
|
# This prevents 404s when workflows set DEVX_REPO_NAME to the full path.
|
||||||
if "/" in repo and owner is None:
|
if "/" in repo and owner is None:
|
||||||
parts = repo.split("/", 1)
|
parts = repo.split("/", 1)
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ def detect_package_name(repo_root: Path) -> str | None:
|
|||||||
Looks for the first subdirectory under ``src/`` that contains
|
Looks for the first subdirectory under ``src/`` that contains
|
||||||
an ``__init__.py`` file with ``__version__``.
|
an ``__init__.py`` file with ``__version__``.
|
||||||
|
|
||||||
Returns the package directory name (e.g., ``devx``,
|
Returns the package directory name (e.g., ``devx``) or ``None`` if
|
||||||
``gitea_runner_manager``) or ``None`` if no package is found.
|
no package is found.
|
||||||
"""
|
"""
|
||||||
src_dir = repo_root / "src"
|
src_dir = repo_root / "src"
|
||||||
if not src_dir.is_dir():
|
if not src_dir.is_dir():
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ class TestMain:
|
|||||||
args = mock_client.ensure_branch_protection.call_args
|
args = mock_client.ensure_branch_protection.call_args
|
||||||
assert args[0][0] == "develop"
|
assert args[0][0] == "develop"
|
||||||
|
|
||||||
@patch.dict("os.environ", {"CI_GITEA_TOKEN": "tok", "DEVX_REPO_NAME": "oblachno/infra"}, clear=True)
|
@patch.dict("os.environ", {"CI_GITEA_TOKEN": "tok", "DEVX_REPO_NAME": "my-org/my-repo"}, clear=True)
|
||||||
@patch("devx.tools.configure_repo.GiteaClient")
|
@patch("devx.tools.configure_repo.GiteaClient")
|
||||||
def test_main_parses_owner_repo_from_env(self, mock_client_cls: MagicMock) -> None:
|
def test_main_parses_owner_repo_from_env(self, mock_client_cls: MagicMock) -> None:
|
||||||
"""DEVX_REPO_NAME with 'owner/repo' format should be split."""
|
"""DEVX_REPO_NAME with 'owner/repo' format should be split."""
|
||||||
@@ -197,15 +197,15 @@ class TestMain:
|
|||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
# Verify GiteaClient was constructed with parsed owner and repo (positional)
|
# Verify GiteaClient was constructed with parsed owner and repo (positional)
|
||||||
call_args = mock_client_cls.call_args
|
call_args = mock_client_cls.call_args
|
||||||
assert call_args[0][2] == "oblachno" # owner is 3rd positional arg
|
assert call_args[0][2] == "my-org" # owner is 3rd positional arg
|
||||||
assert call_args[0][3] == "infra" # repo is 4th positional arg
|
assert call_args[0][3] == "my-repo" # repo is 4th positional arg
|
||||||
|
|
||||||
@patch.dict(
|
@patch.dict(
|
||||||
"os.environ",
|
"os.environ",
|
||||||
{"CI_GITEA_TOKEN": "tok", "DEVX_REPO_NAME": "infra", "DEVX_REPO_OWNER": "oblachno"},
|
{"CI_GITEA_TOKEN": "tok", "DEVX_REPO_NAME": "my-repo", "DEVX_REPO_OWNER": "my-org"},
|
||||||
clear=True,
|
clear=True,
|
||||||
)
|
)
|
||||||
@patch("devx.tools.configure_repo.REPO_OWNER", "oblachno")
|
@patch("devx.tools.configure_repo.REPO_OWNER", "my-org")
|
||||||
@patch("devx.tools.configure_repo.GiteaClient")
|
@patch("devx.tools.configure_repo.GiteaClient")
|
||||||
def test_main_no_slash_when_owner_set_separately(self, mock_client_cls: MagicMock) -> None:
|
def test_main_no_slash_when_owner_set_separately(self, mock_client_cls: MagicMock) -> None:
|
||||||
"""When DEVX_REPO_OWNER is set, DEVX_REPO_NAME should not be split."""
|
"""When DEVX_REPO_OWNER is set, DEVX_REPO_NAME should not be split."""
|
||||||
@@ -216,12 +216,12 @@ class TestMain:
|
|||||||
result = runner.invoke(main, [])
|
result = runner.invoke(main, [])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
call_args = mock_client_cls.call_args
|
call_args = mock_client_cls.call_args
|
||||||
assert call_args[0][2] == "oblachno" # owner
|
assert call_args[0][2] == "my-org" # owner
|
||||||
assert call_args[0][3] == "infra" # repo
|
assert call_args[0][3] == "my-repo" # repo
|
||||||
|
|
||||||
@patch.dict("os.environ", {"CI_GITEA_TOKEN": "tok"}, clear=True)
|
@patch.dict("os.environ", {"CI_GITEA_TOKEN": "tok"}, clear=True)
|
||||||
@patch("devx.tools.configure_repo.REPO_NAME", "devx")
|
@patch("devx.tools.configure_repo.REPO_NAME", "devx")
|
||||||
@patch("devx.tools.configure_repo.REPO_OWNER", "oblachno-oss")
|
@patch("devx.tools.configure_repo.REPO_OWNER", "my-org")
|
||||||
@patch("devx.tools.configure_repo.GiteaClient")
|
@patch("devx.tools.configure_repo.GiteaClient")
|
||||||
def test_main_repo_from_pyproject(self, mock_client_cls: MagicMock) -> None:
|
def test_main_repo_from_pyproject(self, mock_client_cls: MagicMock) -> None:
|
||||||
"""When no env var is set, repo name should come from pyproject.toml."""
|
"""When no env var is set, repo name should come from pyproject.toml."""
|
||||||
@@ -232,5 +232,5 @@ class TestMain:
|
|||||||
result = runner.invoke(main, [])
|
result = runner.invoke(main, [])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
call_args = mock_client_cls.call_args
|
call_args = mock_client_cls.call_args
|
||||||
assert call_args[0][2] == "oblachno-oss" # owner
|
assert call_args[0][2] == "my-org" # owner
|
||||||
assert call_args[0][3] == "devx" # repo
|
assert call_args[0][3] == "devx" # repo
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ class TestExtractTaskId:
|
|||||||
|
|
||||||
|
|
||||||
class TestGetRepoName:
|
class TestGetRepoName:
|
||||||
@patch.dict("os.environ", {"DEVX_REPO_NAME": "infra"})
|
@patch.dict("os.environ", {"DEVX_REPO_NAME": "my-repo"})
|
||||||
def test_from_env(self) -> None:
|
def test_from_env(self) -> None:
|
||||||
assert get_repo_name() == "infra"
|
assert get_repo_name() == "my-repo"
|
||||||
|
|
||||||
@patch("devx.tools.create_pr.REPO_NAME", "devx")
|
@patch("devx.tools.create_pr.REPO_NAME", "devx")
|
||||||
@patch.dict("os.environ", {"GITHUB_REPOSITORY": "oblachno/infra"}, clear=True)
|
@patch.dict("os.environ", {"GITHUB_REPOSITORY": "my-org/my-repo"}, clear=True)
|
||||||
def test_env_overrides_pyproject(self) -> None:
|
def test_env_overrides_pyproject(self) -> None:
|
||||||
assert get_repo_name() == "infra"
|
assert get_repo_name() == "my-repo"
|
||||||
|
|
||||||
@patch("devx.tools.create_pr.REPO_NAME", "devx")
|
@patch("devx.tools.create_pr.REPO_NAME", "devx")
|
||||||
@patch.dict("os.environ", {}, clear=True)
|
@patch.dict("os.environ", {}, clear=True)
|
||||||
@@ -40,9 +40,9 @@ class TestGetRepoName:
|
|||||||
assert get_repo_name() == "devx"
|
assert get_repo_name() == "devx"
|
||||||
|
|
||||||
@patch("devx.tools.create_pr.REPO_NAME", "")
|
@patch("devx.tools.create_pr.REPO_NAME", "")
|
||||||
@patch.dict("os.environ", {"GITHUB_REPOSITORY": "oblachno/infra"}, clear=True)
|
@patch.dict("os.environ", {"GITHUB_REPOSITORY": "my-org/my-repo"}, clear=True)
|
||||||
def test_from_github(self) -> None:
|
def test_from_github(self) -> None:
|
||||||
assert get_repo_name() == "infra"
|
assert get_repo_name() == "my-repo"
|
||||||
|
|
||||||
@patch("devx.tools.create_pr.REPO_NAME", "")
|
@patch("devx.tools.create_pr.REPO_NAME", "")
|
||||||
@patch.dict("os.environ", {}, clear=True)
|
@patch.dict("os.environ", {}, clear=True)
|
||||||
|
|||||||
@@ -592,7 +592,7 @@ class TestLptDistribute:
|
|||||||
def test_load_balance_with_varying_weights(self) -> None:
|
def test_load_balance_with_varying_weights(self) -> None:
|
||||||
"""LPT should produce better load balance than round-robin."""
|
"""LPT should produce better load balance than round-robin."""
|
||||||
items = list(range(7))
|
items = list(range(7))
|
||||||
# Simulate infra-like weights: 2 heavy, 2 medium, 3 light
|
# Simulate multi-role-like weights: 2 heavy, 2 medium, 3 light
|
||||||
weights = [10, 10, 7, 7, 3, 3, 3]
|
weights = [10, 10, 7, 7, 3, 3, 3]
|
||||||
groups = _lpt_distribute(items, weights, 3)
|
groups = _lpt_distribute(items, weights, 3)
|
||||||
loads = [sum(weights[i] for i in g) for g in groups]
|
loads = [sum(weights[i] for i in g) for g in groups]
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ class TestCli:
|
|||||||
"RUN_ID": "123",
|
"RUN_ID": "123",
|
||||||
"JOB_NAME": "integration-tests",
|
"JOB_NAME": "integration-tests",
|
||||||
"MATRIX_INDEX": "0",
|
"MATRIX_INDEX": "0",
|
||||||
"GITEA_REPOSITORY": "oblachno-oss/infra",
|
"GITEA_REPOSITORY": "my-org/my-repo",
|
||||||
"PATH": os.environ.get("PATH", ""),
|
"PATH": os.environ.get("PATH", ""),
|
||||||
},
|
},
|
||||||
clear=True,
|
clear=True,
|
||||||
@@ -152,7 +152,7 @@ class TestCli:
|
|||||||
"RUN_ID": "123",
|
"RUN_ID": "123",
|
||||||
"JOB_NAME": "integration-tests",
|
"JOB_NAME": "integration-tests",
|
||||||
"MATRIX_INDEX": "0",
|
"MATRIX_INDEX": "0",
|
||||||
"GITEA_REPOSITORY": "oblachno-oss/infra",
|
"GITEA_REPOSITORY": "my-org/my-repo",
|
||||||
"PATH": os.environ.get("PATH", ""),
|
"PATH": os.environ.get("PATH", ""),
|
||||||
},
|
},
|
||||||
clear=True,
|
clear=True,
|
||||||
@@ -197,7 +197,7 @@ class TestCli:
|
|||||||
"RUN_ID": "123",
|
"RUN_ID": "123",
|
||||||
"JOB_NAME": "integration-tests",
|
"JOB_NAME": "integration-tests",
|
||||||
"MATRIX_INDEX": "0",
|
"MATRIX_INDEX": "0",
|
||||||
"GITEA_REPOSITORY": "oblachno-oss/infra",
|
"GITEA_REPOSITORY": "my-org/my-repo",
|
||||||
"PATH": os.environ.get("PATH", ""),
|
"PATH": os.environ.get("PATH", ""),
|
||||||
},
|
},
|
||||||
clear=True,
|
clear=True,
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ class TestCli:
|
|||||||
"RUN_ID": "123",
|
"RUN_ID": "123",
|
||||||
"JOB_NAME": "molecule-tests",
|
"JOB_NAME": "molecule-tests",
|
||||||
"MATRIX_INDEX": "0",
|
"MATRIX_INDEX": "0",
|
||||||
"GITEA_REPOSITORY": "oblachno-oss/grm",
|
"GITEA_REPOSITORY": "my-org/my-repo",
|
||||||
"PATH": os.environ.get("PATH", ""),
|
"PATH": os.environ.get("PATH", ""),
|
||||||
},
|
},
|
||||||
clear=True,
|
clear=True,
|
||||||
@@ -292,7 +292,7 @@ class TestCli:
|
|||||||
"RUN_ID": "123",
|
"RUN_ID": "123",
|
||||||
"JOB_NAME": "molecule-tests",
|
"JOB_NAME": "molecule-tests",
|
||||||
"MATRIX_INDEX": "0",
|
"MATRIX_INDEX": "0",
|
||||||
"GITEA_REPOSITORY": "oblachno-oss/grm",
|
"GITEA_REPOSITORY": "my-org/my-repo",
|
||||||
"PATH": os.environ.get("PATH", ""),
|
"PATH": os.environ.get("PATH", ""),
|
||||||
},
|
},
|
||||||
clear=True,
|
clear=True,
|
||||||
@@ -375,7 +375,7 @@ class TestCli:
|
|||||||
"RUN_ID": "123",
|
"RUN_ID": "123",
|
||||||
"JOB_NAME": "molecule-tests",
|
"JOB_NAME": "molecule-tests",
|
||||||
"MATRIX_INDEX": "0",
|
"MATRIX_INDEX": "0",
|
||||||
"GITEA_REPOSITORY": "oblachno-oss/grm",
|
"GITEA_REPOSITORY": "my-org/my-repo",
|
||||||
"PATH": os.environ.get("PATH", ""),
|
"PATH": os.environ.get("PATH", ""),
|
||||||
},
|
},
|
||||||
clear=True,
|
clear=True,
|
||||||
@@ -422,7 +422,7 @@ class TestCli:
|
|||||||
"RUN_ID": "123",
|
"RUN_ID": "123",
|
||||||
"JOB_NAME": "molecule-tests",
|
"JOB_NAME": "molecule-tests",
|
||||||
"MATRIX_INDEX": "0",
|
"MATRIX_INDEX": "0",
|
||||||
"GITEA_REPOSITORY": "oblachno-oss/grm",
|
"GITEA_REPOSITORY": "my-org/my-repo",
|
||||||
"PATH": os.environ.get("PATH", ""),
|
"PATH": os.environ.get("PATH", ""),
|
||||||
},
|
},
|
||||||
clear=True,
|
clear=True,
|
||||||
|
|||||||
@@ -713,7 +713,7 @@ class TestMain:
|
|||||||
def test_dry_run_does_not_post(self, mock_client_class: MagicMock, mock_run: MagicMock) -> None:
|
def test_dry_run_does_not_post(self, mock_client_class: MagicMock, mock_run: MagicMock) -> None:
|
||||||
mock_run.return_value = ReviewResult()
|
mock_run.return_value = ReviewResult()
|
||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
result = runner.invoke(main, ["42", "oblachno-oss/grm", "--dry-run"], env={"CI_GITEA_TOKEN": "fake"})
|
result = runner.invoke(main, ["42", "my-org/my-repo", "--dry-run"], env={"CI_GITEA_TOKEN": "fake"})
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert "[dry-run]" in result.output
|
assert "[dry-run]" in result.output
|
||||||
mock_client_class.return_value.create_review.assert_not_called()
|
mock_client_class.return_value.create_review.assert_not_called()
|
||||||
@@ -724,7 +724,7 @@ class TestMain:
|
|||||||
mock_run.return_value = ReviewResult()
|
mock_run.return_value = ReviewResult()
|
||||||
mock_client_class.return_value.create_review.return_value = {"id": 123}
|
mock_client_class.return_value.create_review.return_value = {"id": 123}
|
||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
result = runner.invoke(main, ["42", "oblachno-oss/grm"], env={"CI_GITEA_TOKEN": "fake"})
|
result = runner.invoke(main, ["42", "my-org/my-repo"], env={"CI_GITEA_TOKEN": "fake"})
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert "Review #123" in result.output
|
assert "Review #123" in result.output
|
||||||
mock_client_class.return_value.create_review.assert_called_once()
|
mock_client_class.return_value.create_review.assert_called_once()
|
||||||
@@ -740,7 +740,7 @@ class TestMain:
|
|||||||
{"id": 124},
|
{"id": 124},
|
||||||
]
|
]
|
||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
result = runner.invoke(main, ["42", "oblachno-oss/grm"], env={"CI_GITEA_TOKEN": "fake"})
|
result = runner.invoke(main, ["42", "my-org/my-repo"], env={"CI_GITEA_TOKEN": "fake"})
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert "Review #124" in result.output
|
assert "Review #124" in result.output
|
||||||
assert client.create_review.call_count == 2
|
assert client.create_review.call_count == 2
|
||||||
@@ -753,12 +753,12 @@ class TestMain:
|
|||||||
client = mock_client_class.return_value
|
client = mock_client_class.return_value
|
||||||
client.create_review.side_effect = APIError(500, "Internal server error")
|
client.create_review.side_effect = APIError(500, "Internal server error")
|
||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
result = runner.invoke(main, ["42", "oblachno-oss/grm"], env={"CI_GITEA_TOKEN": "fake"})
|
result = runner.invoke(main, ["42", "my-org/my-repo"], env={"CI_GITEA_TOKEN": "fake"})
|
||||||
assert result.exit_code != 0
|
assert result.exit_code != 0
|
||||||
|
|
||||||
def test_no_token_raises(self) -> None:
|
def test_no_token_raises(self) -> None:
|
||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
result = runner.invoke(main, ["42", "oblachno-oss/grm"], env={"CI_GITEA_TOKEN": ""})
|
result = runner.invoke(main, ["42", "my-org/my-repo"], env={"CI_GITEA_TOKEN": ""})
|
||||||
assert result.exit_code != 0
|
assert result.exit_code != 0
|
||||||
assert "CI_GITEA_TOKEN" in result.output
|
assert "CI_GITEA_TOKEN" in result.output
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class TestPushToBadgesBranch:
|
|||||||
|
|
||||||
class TestUpdateBadgeUrls:
|
class TestUpdateBadgeUrls:
|
||||||
def test_replaces_branch_url(self) -> None:
|
def test_replaces_branch_url(self) -> None:
|
||||||
content = "[]"
|
content = "[]"
|
||||||
result = push_badges.update_badge_urls(content, "abc123def456")
|
result = push_badges.update_badge_urls(content, "abc123def456")
|
||||||
assert "raw/commit/abc123def456/tests.svg" in result
|
assert "raw/commit/abc123def456/tests.svg" in result
|
||||||
assert "raw/branch/badges" not in result
|
assert "raw/branch/badges" not in result
|
||||||
@@ -93,7 +93,7 @@ class TestUpdateBadgeUrls:
|
|||||||
"""Old commit SHA URLs should be replaced with the new one."""
|
"""Old commit SHA URLs should be replaced with the new one."""
|
||||||
old_sha = "aabb123456789012345678901234567890123456" # 40 hex chars
|
old_sha = "aabb123456789012345678901234567890123456" # 40 hex chars
|
||||||
new_sha = "ccdd123456789012345678901234567890123456" # 40 hex chars
|
new_sha = "ccdd123456789012345678901234567890123456" # 40 hex chars
|
||||||
content = f"[]"
|
content = f"[]"
|
||||||
result = push_badges.update_badge_urls(content, new_sha)
|
result = push_badges.update_badge_urls(content, new_sha)
|
||||||
assert f"raw/commit/{new_sha}/tests.svg" in result
|
assert f"raw/commit/{new_sha}/tests.svg" in result
|
||||||
assert old_sha not in result
|
assert old_sha not in result
|
||||||
@@ -105,16 +105,16 @@ class TestUpdateBadgeUrls:
|
|||||||
|
|
||||||
def test_multiple_badges(self) -> None:
|
def test_multiple_badges(self) -> None:
|
||||||
content = (
|
content = (
|
||||||
"[]\n"
|
"[]\n"
|
||||||
"[]\n"
|
"[]\n"
|
||||||
"[]"
|
"[]"
|
||||||
)
|
)
|
||||||
result = push_badges.update_badge_urls(content, "abc123def456")
|
result = push_badges.update_badge_urls(content, "abc123def456")
|
||||||
assert result.count("raw/commit/abc123def456/") == 3
|
assert result.count("raw/commit/abc123def456/") == 3
|
||||||
assert "raw/branch/badges" not in result
|
assert "raw/branch/badges" not in result
|
||||||
|
|
||||||
def test_preserves_non_badge_urls(self) -> None:
|
def test_preserves_non_badge_urls(self) -> None:
|
||||||
content = "[]"
|
content = "[]"
|
||||||
result = push_badges.update_badge_urls(content, "abc123")
|
result = push_badges.update_badge_urls(content, "abc123")
|
||||||
assert result == content
|
assert result == content
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ class TestUpdateBadgeUrls:
|
|||||||
class TestUpdateReadmeWithBadgeSha:
|
class TestUpdateReadmeWithBadgeSha:
|
||||||
def test_updates_readme(self, tmp_path: Path) -> None:
|
def test_updates_readme(self, tmp_path: Path) -> None:
|
||||||
readme = tmp_path / "README.md"
|
readme = tmp_path / "README.md"
|
||||||
readme.write_text("[]")
|
readme.write_text("[]")
|
||||||
with patch("subprocess.run"):
|
with patch("subprocess.run"):
|
||||||
push_badges.update_readme_with_badge_sha("abc123def456", repo_root=tmp_path)
|
push_badges.update_readme_with_badge_sha("abc123def456", repo_root=tmp_path)
|
||||||
content = readme.read_text()
|
content = readme.read_text()
|
||||||
|
|||||||
@@ -1147,7 +1147,7 @@ class TestMain:
|
|||||||
mock_ft: MagicMock,
|
mock_ft: MagicMock,
|
||||||
mock_vtc: MagicMock,
|
mock_vtc: MagicMock,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Release is skipped when only workflow/infra files changed."""
|
"""Release is skipped when only workflow/infrastructure files changed."""
|
||||||
mock_run_cmd.return_value = MagicMock(returncode=0, stdout="master\n", stderr="")
|
mock_run_cmd.return_value = MagicMock(returncode=0, stdout="master\n", stderr="")
|
||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
result = runner.invoke(main, [])
|
result = runner.invoke(main, [])
|
||||||
|
|||||||
@@ -166,9 +166,9 @@ class TestCustomPrefix:
|
|||||||
f.write(content)
|
f.write(content)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
@patch.dict("os.environ", {"DEVX_TASK_PREFIX": "GRM"})
|
@patch.dict("os.environ", {"DEVX_TASK_PREFIX": "PROJ"})
|
||||||
def test_master_accepts_grm_prefix(self) -> None:
|
def test_master_accepts_proj_prefix(self) -> None:
|
||||||
"""Master branch accepts GRM-N: prefix when DEVX_TASK_PREFIX=GRM."""
|
"""Master branch accepts PROJ-N: prefix when DEVX_TASK_PREFIX=GRM."""
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
import devx.ci.validate_commit_msg as vcm
|
import devx.ci.validate_commit_msg as vcm
|
||||||
@@ -177,7 +177,7 @@ class TestCustomPrefix:
|
|||||||
importlib.reload(devx.config)
|
importlib.reload(devx.config)
|
||||||
importlib.reload(vcm)
|
importlib.reload(vcm)
|
||||||
try:
|
try:
|
||||||
msg_path = self._write_msg("GRM-66: fix: add scripts/** to infrastructure")
|
msg_path = self._write_msg("PROJ-66: fix: add scripts/** to infrastructure")
|
||||||
with patch("devx.ci.validate_commit_msg.get_branch", return_value="master"):
|
with patch("devx.ci.validate_commit_msg.get_branch", return_value="master"):
|
||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
result = runner.invoke(vcm.main, [msg_path])
|
result = runner.invoke(vcm.main, [msg_path])
|
||||||
@@ -188,8 +188,8 @@ class TestCustomPrefix:
|
|||||||
importlib.reload(devx.config)
|
importlib.reload(devx.config)
|
||||||
importlib.reload(vcm)
|
importlib.reload(vcm)
|
||||||
|
|
||||||
@patch.dict("os.environ", {"DEVX_TASK_PREFIX": "GRM"})
|
@patch.dict("os.environ", {"DEVX_TASK_PREFIX": "PROJ"})
|
||||||
def test_master_rejects_devx_prefix_when_grm_configured(self) -> None:
|
def test_master_rejects_devx_prefix_when_proj_configured(self) -> None:
|
||||||
"""Master branch rejects DEVX-N: prefix when DEVX_TASK_PREFIX=GRM."""
|
"""Master branch rejects DEVX-N: prefix when DEVX_TASK_PREFIX=GRM."""
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
@@ -204,16 +204,16 @@ class TestCustomPrefix:
|
|||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
result = runner.invoke(vcm.main, [msg_path])
|
result = runner.invoke(vcm.main, [msg_path])
|
||||||
assert result.exit_code == 1
|
assert result.exit_code == 1
|
||||||
assert "GRM-N" in result.output
|
assert "PROJ-N" in result.output
|
||||||
os.unlink(msg_path)
|
os.unlink(msg_path)
|
||||||
finally:
|
finally:
|
||||||
os.environ.pop("DEVX_TASK_PREFIX", None)
|
os.environ.pop("DEVX_TASK_PREFIX", None)
|
||||||
importlib.reload(devx.config)
|
importlib.reload(devx.config)
|
||||||
importlib.reload(vcm)
|
importlib.reload(vcm)
|
||||||
|
|
||||||
@patch.dict("os.environ", {"DEVX_TASK_PREFIX": "GRM"})
|
@patch.dict("os.environ", {"DEVX_TASK_PREFIX": "PROJ"})
|
||||||
def test_feature_branch_rejects_grm_prefix(self) -> None:
|
def test_feature_branch_rejects_proj_prefix(self) -> None:
|
||||||
"""Feature branch rejects GRM-N: prefix when DEVX_TASK_PREFIX=GRM."""
|
"""Feature branch rejects PROJ-N: prefix when DEVX_TASK_PREFIX=GRM."""
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
import devx.ci.validate_commit_msg as vcm
|
import devx.ci.validate_commit_msg as vcm
|
||||||
@@ -222,8 +222,8 @@ class TestCustomPrefix:
|
|||||||
importlib.reload(devx.config)
|
importlib.reload(devx.config)
|
||||||
importlib.reload(vcm)
|
importlib.reload(vcm)
|
||||||
try:
|
try:
|
||||||
msg_path = self._write_msg("GRM-66: fix: should not have prefix on branch")
|
msg_path = self._write_msg("PROJ-66: fix: should not have prefix on branch")
|
||||||
with patch("devx.ci.validate_commit_msg.get_branch", return_value="GRM-66-fix"):
|
with patch("devx.ci.validate_commit_msg.get_branch", return_value="PROJ-66-fix"):
|
||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
result = runner.invoke(vcm.main, [msg_path])
|
result = runner.invoke(vcm.main, [msg_path])
|
||||||
assert result.exit_code == 1
|
assert result.exit_code == 1
|
||||||
|
|||||||
Reference in New Issue
Block a user