DEVX-13: feat: add per-test timing quality gate to check_test_speed
Post-merge / detect-type (push) Successful in 12s
Post-merge / validate-commit-msg (push) Successful in 10s
Post-merge / configure-repo (push) Successful in 26s
Post-merge / release (push) Successful in 43s
Post-merge / vikunja (push) Successful in 16s
Post-merge / sync-wiki (push) Successful in 48s
Post-merge / badges (push) Successful in 58s

This commit was merged in pull request #24.
This commit is contained in:
2026-06-23 16:25:50 +00:00
parent 547fef4f27
commit c20dfd185a
9 changed files with 1488 additions and 1225 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
PYTHONPATH: src
run: |
. .venv/bin/activate
python3 -m devx.tools.check_test_speed --max-seconds 60
python3 -m devx.tools.check_test_speed --max-seconds 60 --max-single-seconds 2.0
- name: Documentation coverage check
env:
PYTHONPATH: src
+1 -1
View File
@@ -1 +1 @@
DEVX-12
DEVX-13
+7 -1
View File
@@ -76,7 +76,13 @@ Validate commit messages for conventional commit format.
### `devx tools check-test-speed`
Run unit tests and enforce a maximum execution-time budget.
Run unit tests and enforce execution-time budgets:
- **Total suite time** must not exceed `--max-seconds` (default: 10s).
- **Per-test time** — no individual test may exceed `--max-single-seconds` (default: 0.5s, 0 to disable).
```bash
python3 -m devx.tools.check_test_speed --max-seconds 10 --max-single-seconds 0.5
```
### `devx tools configure-repo`
+4 -3
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
# pre-commit hook: fail if unit tests take longer than 10 seconds.
# Aligned with CI timeout (ci.yml uses --max-seconds 10).
# pre-commit hook: fail if unit tests are too slow.
# Checks both total suite time (10s) and per-test time (0.5s).
# Aligned with CI (ci.yml uses same thresholds).
set -e
export PYTHONPATH=src
python3 -m devx.tools.check_test_speed --max-seconds 10
python3 -m devx.tools.check_test_speed --max-seconds 10 --max-single-seconds 0.5
+95 -11
View File
@@ -1,12 +1,21 @@
#!/usr/bin/env python3
"""Run unit tests and enforce a maximum execution-time budget.
"""Run unit tests and enforce execution-time budgets.
Checks two quality gates:
1. **Total suite time** must not exceed ``--max-seconds``.
2. **Per-test time** — no individual test may exceed ``--max-single-seconds``.
Usage:
python3 -m devx.tools.check_test_speed [--max-seconds N]
python3 -m devx.tools.check_test_speed [--max-seconds N] [--max-single-seconds S]
The module runs ``make test-unit`` with ``PYTEST_ADDOPTS=--durations=0`` so
that pytest emits per-test timing lines alongside the summary. Both the
total wall-clock time and individual test durations are parsed and validated.
"""
from __future__ import annotations
import os
import re
import subprocess # nosec B404
@@ -14,18 +23,32 @@ import click
from devx.i18n import _
DEFAULT_MAX_SECONDS = 2.0
DEFAULT_MAX_SECONDS = 10.0
DEFAULT_MAX_SINGLE_SECONDS = 0.5
TEST_COMMAND = ["make", "test-unit"]
# Matches pytest summary line: "234 passed in 0.70s"
_TIMING_RE = re.compile(r"(\d+) passed.* in ([0-9.]+)s")
# Matches per-test duration lines from --durations=0:
# 0.51s call tests/test_foo.py::test_bar
_DURATION_LINE_RE = re.compile(r"^(\d+\.?\d*)s\s+(?:setup|call|teardown)\s+(.+)$")
def run_tests() -> tuple[str, str]:
"""Execute the unit-test suite and return (stdout, stderr)."""
"""Execute the unit-test suite and return (stdout, stderr).
Sets ``PYTEST_ADDOPTS=--durations=0`` so pytest emits per-test timings.
"""
env = os.environ.copy()
existing = env.get("PYTEST_ADDOPTS", "")
env["PYTEST_ADDOPTS"] = f"--durations=0 {existing}".strip()
result = subprocess.run( # nosec B603
TEST_COMMAND,
capture_output=True,
text=True,
check=False,
env=env,
)
return result.stdout, result.stderr
@@ -43,8 +66,23 @@ def parse_duration(output: str) -> float:
raise click.ClickException(_("Could not parse test execution time from output."))
def parse_per_test_durations(output: str) -> list[tuple[str, float]]:
"""Extract per-test timings from ``--durations=0`` output.
Returns a list of ``(test_name, seconds)`` tuples sorted by duration
(slowest first).
"""
durations: list[tuple[str, float]] = []
for line in output.splitlines():
match = _DURATION_LINE_RE.match(line.strip())
if match:
durations.append((match.group(2).strip(), float(match.group(1))))
durations.sort(key=lambda x: x[1], reverse=True)
return durations
def check_speed(duration: float, max_seconds: float) -> None:
"""Validate duration is within budget; raise on violation."""
"""Validate total duration is within budget; raise on violation."""
if duration > max_seconds:
raise click.ClickException(
_(
@@ -57,19 +95,58 @@ def check_speed(duration: float, max_seconds: float) -> None:
)
def main(max_seconds: float) -> None:
"""Run tests, parse timing, and enforce the budget."""
def check_per_test_speed(
durations: list[tuple[str, float]],
max_single_seconds: float,
) -> list[str]:
"""Return a list of violation messages for tests exceeding the per-test limit.
An empty list means all tests are within budget.
"""
violations: list[str] = []
for name, elapsed in durations:
if elapsed > max_single_seconds:
violations.append(
_(
"Test '{name}' took {elapsed:.2f}s (limit: {limit}s). "
"Optimise: use lighter fixtures, reduce I/O, or mock external calls.",
name=name,
elapsed=elapsed,
limit=max_single_seconds,
)
)
return violations
def main(max_seconds: float, max_single_seconds: float) -> None:
"""Run tests, parse timings, and enforce both budgets."""
stdout, stderr = run_tests()
combined = stdout + "\n" + stderr
click.echo(combined, err=False)
duration = parse_duration(combined)
check_speed(duration, max_seconds)
if max_single_seconds > 0:
per_test = parse_per_test_durations(combined)
violations = check_per_test_speed(per_test, max_single_seconds)
if violations:
msg = _(
"Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.",
count=len(violations),
limit=max_single_seconds,
)
click.echo(f"\n{msg}", err=True)
for v in violations:
click.echo(f" - {v}", err=True)
raise click.ClickException(msg)
click.echo(
_(
"Unit tests passed in {duration:.2f}s (under {max}s limit).",
"Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit).",
duration=duration,
max=max_seconds,
single=max_single_seconds,
)
)
@@ -80,10 +157,17 @@ def main(max_seconds: float) -> None:
type=float,
default=DEFAULT_MAX_SECONDS,
show_default=True,
help="Maximum allowed execution time in seconds.",
help="Maximum allowed total execution time in seconds.",
)
def cli(max_seconds: float) -> None:
main(max_seconds)
@click.option(
"--max-single-seconds",
type=float,
default=DEFAULT_MAX_SINGLE_SECONDS,
show_default=True,
help="Maximum allowed per-test time in seconds (0 to disable).",
)
def cli(max_seconds: float, max_single_seconds: float) -> None:
main(max_seconds, max_single_seconds)
if __name__ == "__main__": # pragma: no cover
+1211 -1197
View File
@@ -1,1199 +1,1213 @@
{
"\n=== Summary ===": {
"en": "\n=== Summary ===",
"bg": "\n=== Summary ===",
"de": "\n=== Summary ===",
"ru": "\n=== Summary ===",
"zh": "\n=== Summary ==="
},
"\nAll documentation coverage checks passed!": {
"en": "\nAll documentation coverage checks passed!",
"bg": "\nAll documentation coverage checks passed!",
"de": "\nAll documentation coverage checks passed!",
"ru": "\nAll documentation coverage checks passed!",
"zh": "\nAll documentation coverage checks passed!"
},
"\nCHANGELOG version ordering:": {
"en": "\nCHANGELOG version ordering:",
"bg": "\nCHANGELOG version ordering:",
"de": "\nCHANGELOG version ordering:",
"ru": "\nCHANGELOG version ordering:",
"zh": "\nCHANGELOG version ordering:"
},
"\nChecking CI script documentation in ci-cd-workflow.md...": {
"en": "\nChecking CI script documentation in ci-cd-workflow.md...",
"bg": "\nChecking CI script documentation in ci-cd-workflow.md...",
"de": "\nChecking CI script documentation in ci-cd-workflow.md...",
"ru": "\nChecking CI script documentation in ci-cd-workflow.md...",
"zh": "\nChecking CI script documentation in ci-cd-workflow.md..."
},
"\nChecking module documentation in architecture.md...": {
"en": "\nChecking module documentation in architecture.md...",
"bg": "\nChecking module documentation in architecture.md...",
"de": "\nChecking module documentation in architecture.md...",
"ru": "\nChecking module documentation in architecture.md...",
"zh": "\nChecking module documentation in architecture.md..."
},
"\nDoc coverage: {covered}/{total} ({pct}%)": {
"en": "\nDoc coverage: {covered}/{total} ({pct}%)",
"bg": "\nDoc coverage: {covered}/{total} ({pct}%)",
"de": "\nDoc coverage: {covered}/{total} ({pct}%)",
"ru": "\nDoc coverage: {covered}/{total} ({pct}%)",
"zh": "\nDoc coverage: {covered}/{total} ({pct}%)"
},
"\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}": {
"en": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
"bg": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
"de": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
"ru": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
"zh": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}"
},
"\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.": {
"en": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.",
"bg": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.",
"de": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.",
"ru": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.",
"zh": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce."
},
"\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.": {
"en": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.",
"bg": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.",
"de": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.",
"ru": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.",
"zh": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report."
},
"\nIntegrity check FAILED ({count} issues):": {
"en": "\nIntegrity check FAILED ({count} issues):",
"bg": "\nIntegrity check FAILED ({count} issues):",
"de": "\nIntegrity check FAILED ({count} issues):",
"ru": "\nIntegrity check FAILED ({count} issues):",
"zh": "\nIntegrity check FAILED ({count} issues):"
},
"\nIntegrity check passed — all {count} pages verified.": {
"en": "\nIntegrity check passed — all {count} pages verified.",
"bg": "\nIntegrity check passed — all {count} pages verified.",
"de": "\nIntegrity check passed — all {count} pages verified.",
"ru": "\nIntegrity check passed — all {count} pages verified.",
"zh": "\nIntegrity check passed — all {count} pages verified."
},
"\nLatest tag: {tag}": {
"en": "\nLatest tag: {tag}",
"bg": "\nLatest tag: {tag}",
"de": "\nLatest tag: {tag}",
"ru": "\nLatest tag: {tag}",
"zh": "\nLatest tag: {tag}"
},
"\nMissing documentation:": {
"en": "\nMissing documentation:",
"bg": "\nMissing documentation:",
"de": "\nMissing documentation:",
"ru": "\nMissing documentation:",
"zh": "\nMissing documentation:"
},
"\nResult: {status}": {
"en": "\nResult: {status}",
"bg": "\nResult: {status}",
"de": "\nResult: {status}",
"ru": "\nResult: {status}",
"zh": "\nResult: {status}"
},
"\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).": {
"en": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).",
"bg": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).",
"de": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).",
"ru": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).",
"zh": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments)."
},
"\nRunning full wiki integrity check...": {
"en": "\nRunning full wiki integrity check...",
"bg": "\nRunning full wiki integrity check...",
"de": "\nRunning full wiki integrity check...",
"ru": "\nRunning full wiki integrity check...",
"zh": "\nRunning full wiki integrity check..."
},
"\nTag → Commit alignment:": {
"en": "\nTag → Commit alignment:",
"bg": "\nTag → Commit alignment:",
"de": "\nTag → Commit alignment:",
"ru": "\nTag → Commit alignment:",
"zh": "\nTag → Commit alignment:"
},
"\nUntagged release commits:": {
"en": "\nUntagged release commits:",
"bg": "\nUntagged release commits:",
"de": "\nUntagged release commits:",
"ru": "\nUntagged release commits:",
"zh": "\nUntagged release commits:"
},
"\nUser-facing changes ({count}):": {
"en": "\nUser-facing changes ({count}):",
"bg": "\nUser-facing changes ({count}):",
"de": "\nUser-facing changes ({count}):",
"ru": "\nUser-facing changes ({count}):",
"zh": "\nUser-facing changes ({count}):"
},
"\nVerification FAILED: {failures} page(s) have empty or mismatched content!": {
"en": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
"bg": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
"de": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
"ru": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
"zh": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!"
},
"\nVerification passed — all wiki pages have correct content.": {
"en": "\nVerification passed — all wiki pages have correct content.",
"bg": "\nVerification passed — all wiki pages have correct content.",
"de": "\nVerification passed — all wiki pages have correct content.",
"ru": "\nVerification passed — all wiki pages have correct content.",
"zh": "\nVerification passed — all wiki pages have correct content."
},
"\nVerifying wiki pages have content...": {
"en": "\nVerifying wiki pages have content...",
"bg": "\nVerifying wiki pages have content...",
"de": "\nVerifying wiki pages have content...",
"ru": "\nVerifying wiki pages have content...",
"zh": "\nVerifying wiki pages have content..."
},
"\nWorkflow-only changes ({count}):": {
"en": "\nWorkflow-only changes ({count}):",
"bg": "\nWorkflow-only changes ({count}):",
"de": "\nWorkflow-only changes ({count}):",
"ru": "\nWorkflow-only changes ({count}):",
"zh": "\nWorkflow-only changes ({count}):"
},
"\n[dry-run] Changelog:\n{changelog}": {
"en": "\n[dry-run] Changelog:\n{changelog}",
"bg": "\n[dry-run] Changelog:\n{changelog}",
"de": "\n[dry-run] Changelog:\n{changelog}",
"ru": "\n[dry-run] Changelog:\n{changelog}",
"zh": "\n[dry-run] Changelog:\n{changelog}"
},
"\n{label} files changed ({count}):": {
"en": "\n{label} files changed ({count}):",
"bg": "\n{label} files changed ({count}):",
"de": "\n{label} files changed ({count}):",
"ru": "\n{label} files changed ({count}):",
"zh": "\n{label} files changed ({count}):"
},
"\n{tag} files ({count}):": {
"en": "\n{tag} files ({count}):",
"bg": "\n{tag} files ({count}):",
"de": "\n{tag} files ({count}):",
"ru": "\n{tag} files ({count}):",
"zh": "\n{tag} files ({count}):"
},
" - Auto-delete branch after merge: yes": {
"en": " - Auto-delete branch after merge: yes",
"bg": " - Автоматично изтриване на клон след сливане: да",
"de": " - Branch nach Merge automatisch löschen: ja",
"ru": " - Автоудаление ветки после слияния: да",
"zh": " - 合并后自动删除分支: 是"
},
" - Block outdated branches: yes": {
"en": " - Block outdated branches: yes",
"bg": " - Блокиране на остарели клонове: да",
"de": " - Veraltete Branches blockieren: ja",
"ru": " - Блокировать устаревшие ветки: да",
"zh": " - 阻止过时分支: 是"
},
" - Block rejected reviews: yes": {
"en": " - Block rejected reviews: yes",
"bg": " - Блокиране на отхвърлени рецензии: да",
"de": " - Abgelehnte Reviews blockieren: ja",
"ru": " - Блокировать отклонённые ревью: да",
"zh": " - 阻止被拒绝的审查: 是"
},
" - Direct pushes: BLOCKED (require PR, whitelisted users can push)": {
"en": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)",
"bg": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)",
"de": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)",
"ru": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)",
"zh": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)"
},
" - Dismiss stale approvals: yes": {
"en": " - Dismiss stale approvals: yes",
"bg": " - Анулиране на остарели одобрения: да",
"de": " - Veraltete Genehmigungen ablehnen: ja",
"ru": " - Отклонять устаревшие одобрения: да",
"zh": " - 忽略过时审批: 是"
},
" - Required approvals: {count}": {
"en": " - Required approvals: {count}",
"bg": " - Необходими одобрения: {count}",
"de": " - Erforderliche Genehmigungen: {count}",
"ru": " - Требуемые одобрения: {count}",
"zh": " - 必需审批数: {count}"
},
" - Required status checks: {checks}": {
"en": " - Required status checks: {checks}",
"bg": " - Необходими проверки на състоянието: {checks}",
"de": " - Erforderliche Status-Checks: {checks}",
"ru": " - Требуемые проверки статуса: {checks}",
"zh": " - 必需状态检查: {checks}"
},
" Created: {title}": {
"en": " Created: {title}",
"bg": " Created: {title}",
"de": " Created: {title}",
"ru": " Created: {title}",
"zh": " Created: {title}"
},
" FAIL: {title} — content mismatch or empty!": {
"en": " FAIL: {title} — content mismatch or empty!",
"bg": " FAIL: {title} — content mismatch or empty!",
"de": " FAIL: {title} — content mismatch or empty!",
"ru": " FAIL: {title} — content mismatch or empty!",
"zh": " FAIL: {title} — content mismatch or empty!"
},
" MISSING: devx {cmd}": {
"en": " MISSING: devx {cmd}",
"bg": " ЛИПСВА: devx {cmd}",
"de": " FEHLT: devx {cmd}",
"ru": " ОТСУТСТВУЕТ: devx {cmd}",
"zh": " 缺失: devx {cmd}"
},
" MISSING: {module}": {
"en": " MISSING: {module}",
"bg": " MISSING: {module}",
"de": " MISSING: {module}",
"ru": " MISSING: {module}",
"zh": " MISSING: {module}"
},
" MISSING: {script}": {
"en": " MISSING: {script}",
"bg": " MISSING: {script}",
"de": " MISSING: {script}",
"ru": " MISSING: {script}",
"zh": " MISSING: {script}"
},
" OK: devx {cmd}": {
"en": " OK: devx {cmd}",
"bg": " ОК: devx {cmd}",
"de": " OK: devx {cmd}",
"ru": " ОК: devx {cmd}",
"zh": " 正常: devx {cmd}"
},
" OK: {module}": {
"en": " OK: {module}",
"bg": " OK: {module}",
"de": " OK: {module}",
"ru": " OK: {module}",
"zh": " OK: {module}"
},
" OK: {script}": {
"en": " OK: {script}",
"bg": " OK: {script}",
"de": " OK: {script}",
"ru": " OK: {script}",
"zh": " OK: {script}"
},
" OK: {title} ({chars} chars)": {
"en": " OK: {title} ({chars} chars)",
"bg": " OK: {title} ({chars} chars)",
"de": " OK: {title} ({chars} chars)",
"ru": " OK: {title} ({chars} chars)",
"zh": " OK: {title} ({chars} chars)"
},
" Updated: {title}": {
"en": " Updated: {title}",
"bg": " Updated: {title}",
"de": " Updated: {title}",
"ru": " Updated: {title}",
"zh": " Updated: {title}"
},
"=== Release Alignment Verification ===\n": {
"en": "=== Release Alignment Verification ===\n",
"bg": "=== Release Alignment Verification ===\n",
"de": "=== Release Alignment Verification ===\n",
"ru": "=== Release Alignment Verification ===\n",
"zh": "=== Release Alignment Verification ===\n"
},
"API poll warning: {exc}": {
"en": "API poll warning: {exc}",
"bg": "API poll warning: {exc}",
"de": "API poll warning: {exc}",
"ru": "API poll warning: {exc}",
"zh": "API poll warning: {exc}"
},
"All molecule tests passed.": {
"en": "All molecule tests passed.",
"bg": "All molecule tests passed.",
"de": "All molecule tests passed.",
"ru": "All molecule tests passed.",
"zh": "All molecule tests passed."
},
"Another molecule runner failed. Stopping this runner early.": {
"en": "Another molecule runner failed. Stopping this runner early.",
"bg": "Another molecule runner failed. Stopping this runner early.",
"de": "Another molecule runner failed. Stopping this runner early.",
"ru": "Another molecule runner failed. Stopping this runner early.",
"zh": "Another molecule runner failed. Stopping this runner early."
},
"Bumping version: {current} -> v{new_version}": {
"en": "Bumping version: {current} -> v{new_version}",
"bg": "Bumping version: {current} -> v{new_version}",
"de": "Bumping version: {current} -> v{new_version}",
"ru": "Bumping version: {current} -> v{new_version}",
"zh": "Bumping version: {current} -> v{new_version}"
},
"Checking CLI command documentation...": {
"en": "Checking CLI command documentation...",
"bg": "Checking CLI command documentation...",
"de": "Checking CLI command documentation...",
"ru": "Checking CLI command documentation...",
"zh": "Checking CLI command documentation..."
},
"Command failed ({cmd}): {stderr}": {
"en": "Command failed ({cmd}): {stderr}",
"bg": "Command failed ({cmd}): {stderr}",
"de": "Command failed ({cmd}): {stderr}",
"ru": "Command failed ({cmd}): {stderr}",
"zh": "Command failed ({cmd}): {stderr}"
},
"Comparing {base}..{head} ({count} files changed)": {
"en": "Comparing {base}..{head} ({count} files changed)",
"bg": "Comparing {base}..{head} ({count} files changed)",
"de": "Comparing {base}..{head} ({count} files changed)",
"ru": "Comparing {base}..{head} ({count} files changed)",
"zh": "Comparing {base}..{head} ({count} files changed)"
},
"Configuring branch protection for {branch}...": {
"en": "Configuring branch protection for {branch}...",
"bg": "Конфигуриране на защита на клона {branch}...",
"de": "Konfiguriere Branch-Schutz für {branch}...",
"ru": "Настройка защиты ветки {branch}...",
"zh": "正在配置 {branch} 的分支保护..."
},
"Configuring repository settings...": {
"en": "Configuring repository settings...",
"bg": "Конфигуриране на настройките на хранилището...",
"de": "Repository-Einstellungen konfigurieren...",
"ru": "Настройка параметров репозитория...",
"zh": "正在配置仓库设置..."
},
"Could not extract conventional commit message from PR commits.": {
"en": "Could not extract conventional commit message from PR commits.",
"bg": "Could not extract conventional commit message from PR commits.",
"de": "Could not extract conventional commit message from PR commits.",
"ru": "Could not extract conventional commit message from PR commits.",
"zh": "Could not extract conventional commit message from PR commits."
},
"Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.": {
"en": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.",
"bg": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.",
"de": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.",
"ru": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.",
"zh": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task."
},
"Could not find __version__ in {file}": {
"en": "Could not find __version__ in {file}",
"bg": "Could not find __version__ in {file}",
"de": "Could not find __version__ in {file}",
"ru": "Could not find __version__ in {file}",
"zh": "Could not find __version__ in {file}"
},
"Could not parse test execution time from output.": {
"en": "Could not parse test execution time from output.",
"bg": "Could not parse test execution time from output.",
"de": "Could not parse test execution time from output.",
"ru": "Could not parse test execution time from output.",
"zh": "Could not parse test execution time from output."
},
"Created issue #{issue_id}: {title}": {
"en": "Created issue #{issue_id}: {title}",
"bg": "Created issue #{issue_id}: {title}",
"de": "Created issue #{issue_id}: {title}",
"ru": "Created issue #{issue_id}: {title}",
"zh": "Created issue #{issue_id}: {title}"
},
"Created release commit.": {
"en": "Created release commit.",
"bg": "Created release commit.",
"de": "Created release commit.",
"ru": "Created release commit.",
"zh": "Created release commit."
},
"Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.": {
"en": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.",
"bg": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.",
"de": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.",
"ru": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.",
"zh": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently."
},
"ERROR: REPO_TOKEN is not set.": {
"en": "ERROR: REPO_TOKEN is not set.",
"bg": "ГРЕШКА: REPO_TOKEN не е зададен.",
"de": "FEHLER: REPO_TOKEN ist nicht gesetzt.",
"ru": "ОШИБКА: REPO_TOKEN не задан.",
"zh": "错误:未设置 REPO_TOKEN。"
},
"ERROR: Repository name not specified. Use --repo or set DEVX_REPO_NAME.": {
"en": "ERROR: Repository name not specified. Use --repo or set DEVX_REPO_NAME.",
"bg": "ГРЕШКА: Името на хранилището не е указано. Използвайте --repo или задайте DEVX_REPO_NAME.",
"de": "FEHLER: Repository-Name nicht angegeben. Verwenden Sie --repo oder setzen Sie DEVX_REPO_NAME.",
"ru": "ОШИБКА: Имя репозитория не указано. Используйте --repo или задайте DEVX_REPO_NAME.",
"zh": "错误:未指定仓库名称。请使用 --repo 或设置 DEVX_REPO_NAME。"
},
"ERROR: Tag consistency check failed. Existing tags are misaligned:": {
"en": "ERROR: Tag consistency check failed. Existing tags are misaligned:",
"bg": "ERROR: Tag consistency check failed. Existing tags are misaligned:",
"de": "ERROR: Tag consistency check failed. Existing tags are misaligned:",
"ru": "ERROR: Tag consistency check failed. Existing tags are misaligned:",
"zh": "ERROR: Tag consistency check failed. Existing tags are misaligned:"
},
"ERROR: VIKUNJA_TOKEN is not set.": {
"en": "ERROR: VIKUNJA_TOKEN is not set.",
"bg": "ГРЕШКА: VIKUNJA_TOKEN не е зададен.",
"de": "FEHLER: VIKUNJA_TOKEN ist nicht gesetzt.",
"ru": "ОШИБКА: VIKUNJA_TOKEN не задан.",
"zh": "错误:未设置 VIKUNJA_TOKEN。"
},
"ERROR: mapping.json not found at {path}": {
"en": "ERROR: mapping.json not found at {path}",
"bg": "ERROR: mapping.json not found at {path}",
"de": "ERROR: mapping.json not found at {path}",
"ru": "ERROR: mapping.json not found at {path}",
"zh": "ERROR: mapping.json not found at {path}"
},
"FAILED: {pair} exited with code {code}": {
"en": "FAILED: {pair} exited with code {code}",
"bg": "FAILED: {pair} exited with code {code}",
"de": "FAILED: {pair} exited with code {code}",
"ru": "FAILED: {pair} exited with code {code}",
"zh": "FAILED: {pair} exited with code {code}"
},
"Failed to create issue via tea: {error}": {
"en": "Failed to create issue via tea: {error}",
"bg": "Failed to create issue via tea: {error}",
"de": "Failed to create issue via tea: {error}",
"ru": "Failed to create issue via tea: {error}",
"zh": "Failed to create issue via tea: {error}"
},
"Found {count} existing wiki pages.": {
"en": "Found {count} existing wiki pages.",
"bg": "Found {count} existing wiki pages.",
"de": "Found {count} existing wiki pages.",
"ru": "Found {count} existing wiki pages.",
"zh": "Found {count} existing wiki pages."
},
"GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.": {
"en": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.",
"bg": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.",
"de": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.",
"ru": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.",
"zh": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation."
},
"Generated {file} with prefix '{prefix}'.": {
"en": "Generated {file} with prefix '{prefix}'.",
"bg": "Generated {file} with prefix '{prefix}'.",
"de": "Generated {file} with prefix '{prefix}'.",
"ru": "Generated {file} with prefix '{prefix}'.",
"zh": "Generated {file} with prefix '{prefix}'."
},
"HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.": {
"en": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
"bg": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
"de": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
"ru": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
"zh": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag."
},
"HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.": {
"en": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.",
"bg": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.",
"de": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.",
"ru": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.",
"zh": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment."
},
"HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.": {
"en": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.",
"bg": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.",
"de": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.",
"ru": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.",
"zh": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping."
},
"HTTP error: {status} — {message}": {
"en": "HTTP error: {status} — {message}",
"bg": "HTTP грешка: {status} — {message}",
"de": "HTTP-Fehler: {status} — {message}",
"ru": "Ошибка HTTP: {status} — {message}",
"zh": "HTTP 错误: {status} — {message}"
},
"HTTP {status} Forbidden — your token lacks admin rights.\nMake sure the token belongs to a repo owner or organisation admin.\nAlternatively, configure branch protection manually in Settings → Branches.": {
"en": "HTTP {status} Forbidden — your token lacks admin rights.\nMake sure the token belongs to a repo owner or organisation admin.\nAlternatively, configure branch protection manually in Settings → Branches.",
"bg": "HTTP {status} Забранено — вашият токен няма администраторски права.\nУверете се, че токенът принадлежи на собственик на хранилище или администратор на организация.\nАлтернативно, конфигурирайте защитата на клона ръчно в Настройки → Клонове.",
"de": "HTTP {status} Verboten — Ihr Token hat keine Admin-Rechte.\nStellen Sie sicher, dass das Token einem Repository-Besitzer oder Organisations-Admin gehört.\nAlternativ können Sie den Branch-Schutz manuell unter Einstellungen → Branches konfigurieren.",
"ru": "HTTP {status} Запрещено — у вашего токена нет прав администратора.\nУбедитесь, что токен принадлежит владельцу репозитория или администратору организации.\nЛибо настройте защиту ветки вручную в разделе Настройки → Ветки.",
"zh": "HTTP {status} 禁止访问 — 您的令牌缺少管理员权限。\n请确保令牌属于仓库所有者或组织管理员。\n或者,您可以在 设置 → 分支 中手动配置分支保护。"
},
"Head branch is behind master. Pulling and rebasing...": {
"en": "Head branch is behind master. Pulling and rebasing...",
"bg": "Head branch is behind master. Pulling and rebasing...",
"de": "Head branch is behind master. Pulling and rebasing...",
"ru": "Head branch is behind master. Pulling and rebasing...",
"zh": "Head branch is behind master. Pulling and rebasing..."
},
"Infrastructure commit (no DEVX-N task ID), skipping Vikunja update: {msg}": {
"en": "Infrastructure commit (no DEVX-N task ID), skipping Vikunja update: {msg}",
"bg": "Инфраструктурен commit (без идентификатор на задача DEVX-N), пропускаме обновяването на Vikunja: {msg}",
"de": "Infrastruktur-Commit (keine DEVX-N Task-ID), Vikunja-Update wird übersprungen: {msg}",
"ru": "Инфраструктурный коммит (без ID задачи DEVX-N), пропуск обновления Vikunja: {msg}",
"zh": "基础设施提交(无 DEVX-N 任务 ID),跳过 Vikunja 更新: {msg}"
},
"Lint failed — refusing to release. Fix lint errors first.\n{stderr}": {
"en": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}",
"bg": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}",
"de": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}",
"ru": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}",
"zh": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}"
},
"Lint passed.": {
"en": "Lint passed.",
"bg": "Lint passed.",
"de": "Lint passed.",
"ru": "Lint passed.",
"zh": "Lint passed."
},
"Mapped file {file} is empty. Update the content or remove from mapping.json.": {
"en": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
"bg": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
"de": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
"ru": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
"zh": "Mapped file {file} is empty. Update the content or remove from mapping.json."
},
"Mapped file {file} not found. Update mapping.json or create the file.": {
"en": "Mapped file {file} not found. Update mapping.json or create the file.",
"bg": "Mapped file {file} not found. Update mapping.json or create the file.",
"de": "Mapped file {file} not found. Update mapping.json or create the file.",
"ru": "Mapped file {file} not found. Update mapping.json or create the file.",
"zh": "Mapped file {file} not found. Update mapping.json or create the file."
},
"Merge failed after rebase retry: {error}\nPlease rebase the PR manually.": {
"en": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
"bg": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
"de": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
"ru": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
"zh": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually."
},
"Merge failed with HTTP {status}: {message}\nPlease check the PR is ready and you have merge rights.": {
"en": "Merge failed with HTTP {status}: {message}\nPlease check the PR is ready and you have merge rights.",
"bg": "Сливането неуспешно с HTTP {status}: {message}\nПроверете дали PR е готов и имате права за сливане.",
"de": "Merge fehlgeschlagen mit HTTP {status}: {message}\nBitte prüfen Sie, ob der PR bereit ist und Sie Merge-Rechte haben.",
"ru": "Слияние не удалось: HTTP {status}: {message}\nПроверьте, что PR готов и у вас есть права на слияние.",
"zh": "合并失败: HTTP {status}: {message}\n请检查 PR 是否准备就绪且您具有合并权限。"
},
"Module {mod} has no main() function": {
"en": "Module {mod} has no main() function",
"bg": "Модул {mod} няма функция main()",
"de": "Modul {mod} hat keine main()-Funktion",
"ru": "Модуль {mod} не имеет функции main()",
"zh": "模块 {mod} 没有 main() 函数"
},
"Molecule directory not found: {path}": {
"en": "Molecule directory not found: {path}",
"bg": "Директорията на molecule не е намерена: {path}",
"de": "Molecule-Verzeichnis nicht gefunden: {path}",
"ru": "Директория molecule не найдена: {path}",
"zh": "未找到 molecule 目录: {path}"
},
"Nice! Gitea release {tag} created.": {
"en": "Nice! Gitea release {tag} created.",
"bg": "Отлично! Gitea release {tag} е създаден.",
"de": "Prima! Gitea-Release {tag} erstellt.",
"ru": "Отлично! Gitea release {tag} создан.",
"zh": "不错!Gitea release {tag} 已创建。"
},
"Nice! PR #{pr_number} squash-merged with title: {merge_title}": {
"en": "Nice! PR #{pr_number} squash-merged with title: {merge_title}",
"bg": "Отлично! PR #{pr_number} е squash-merge-нат със заглавие: {merge_title}",
"de": "Prima! PR #{pr_number} wurde mit Titel {merge_title} squash-gemergt.",
"ru": "Отлично! PR #{pr_number} squash-merge с заголовком: {merge_title}",
"zh": "不错!PR #{pr_number} 已 squash 合并,标题: {merge_title}"
},
"Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.": {
"en": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.",
"bg": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.",
"de": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.",
"ru": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.",
"zh": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered."
},
"Nice! Vikunja task {task_id} (ID {vikunja_id}) updated and marked done.": {
"en": "Nice! Vikunja task {task_id} (ID {vikunja_id}) updated and marked done.",
"bg": "Отлично! Задача Vikunja {task_id} (ID {vikunja_id}) е обновена и маркирана като готова.",
"de": "Prima! Vikunja-Aufgabe {task_id} (ID {vikunja_id}) aktualisiert und als erledigt markiert.",
"ru": "Отлично! Задача Vikunja {task_id} (ID {vikunja_id}) обновлена и отмечена как выполненная.",
"zh": "不错!Vikunja 任务 {task_id} (ID {vikunja_id}) 已更新并标记为完成。"
},
"No changes between {base} and {head}.": {
"en": "No changes between {base} and {head}.",
"bg": "No changes between {base} and {head}.",
"de": "No changes between {base} and {head}.",
"ru": "No changes between {base} and {head}.",
"zh": "No changes between {base} and {head}."
},
"No staged changes — version and changelog already up to date.": {
"en": "No staged changes — version and changelog already up to date.",
"bg": "No staged changes — version and changelog already up to date.",
"de": "No staged changes — version and changelog already up to date.",
"ru": "No staged changes — version and changelog already up to date.",
"zh": "No staged changes — version and changelog already up to date."
},
"No tags found — treating all changes as user-facing.": {
"en": "No tags found — treating all changes as user-facing.",
"bg": "No tags found — treating all changes as user-facing.",
"de": "No tags found — treating all changes as user-facing.",
"ru": "No tags found — treating all changes as user-facing.",
"zh": "No tags found — treating all changes as user-facing."
},
"No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.": {
"en": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.",
"bg": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.",
"de": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.",
"ru": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.",
"zh": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID."
},
"No unreleased changes found. Nothing to release.": {
"en": "No unreleased changes found. Nothing to release.",
"bg": "No unreleased changes found. Nothing to release.",
"de": "No unreleased changes found. Nothing to release.",
"ru": "No unreleased changes found. Nothing to release.",
"zh": "No unreleased changes found. Nothing to release."
},
"No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.": {
"en": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.",
"bg": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.",
"de": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.",
"ru": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.",
"zh": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release."
},
"Note: Self-approval not allowed. Posting COMMENT instead.": {
"en": "Note: Self-approval not allowed. Posting COMMENT instead.",
"bg": "Note: Self-approval not allowed. Posting COMMENT instead.",
"de": "Note: Self-approval not allowed. Posting COMMENT instead.",
"ru": "Note: Self-approval not allowed. Posting COMMENT instead.",
"zh": "Note: Self-approval not allowed. Posting COMMENT instead."
},
"Oops! Commit message must follow conventional commit format.\n Expected: <type>: <description>\n Got: {subject}\n Allowed types: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE": {
"en": "Oops! Commit message must follow conventional commit format.\n Expected: <type>: <description>\n Got: {subject}\n Allowed types: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"bg": "Опа! Съобщението за commit трябва да следва конвенционален формат.\n Очаква се: <type>: <description>\n Получено: {subject}\n Разрешени типове: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"de": "Ups! Commit-Nachricht muss dem konventionellen Commit-Format folgen.\n Erwartet: <type>: <description>\n Erhalten: {subject}\n Erlaubte Typen: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"ru": "Ой! Сообщение коммита должно соответствовать формату conventional commit.\n Ожидается: <type>: <description>\n Получено: {subject}\n Допустимые типы: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"zh": "哎呀!提交消息必须遵循 conventional commit 格式。\n 预期格式: <type>: <description>\n 实际: {subject}\n 允许的类型: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE"
},
"Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.": {
"en": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.",
"bg": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.",
"de": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.",
"ru": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.",
"zh": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI."
},
"Oops! Gitea PyPI registry publish failed:\n{stderr}": {
"en": "Oops! Gitea PyPI registry publish failed:\n{stderr}",
"bg": "Опа! Публикуването в Gitea PyPI registry неуспешно:\n{stderr}",
"de": "Ups! Veröffentlichung in der Gitea PyPI-Registry fehlgeschlagen:\n{stderr}",
"ru": "Ой! Публикация в Gitea PyPI registry не удалась:\n{stderr}",
"zh": "哎呀!Gitea PyPI registry 发布失败:\n{stderr}"
},
"Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}": {
"en": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}",
"bg": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}",
"de": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}",
"ru": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}",
"zh": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}"
},
"Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}": {
"en": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"bg": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"de": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"ru": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"zh": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}"
},
"Oops! No task ID found in .taskid file or branch name '{branch}'.": {
"en": "Oops! No task ID found in .taskid file or branch name '{branch}'.",
"bg": "Oops! No task ID found in .taskid file or branch name '{branch}'.",
"de": "Oops! No task ID found in .taskid file or branch name '{branch}'.",
"ru": "Oops! No task ID found in .taskid file or branch name '{branch}'.",
"zh": "Oops! No task ID found in .taskid file or branch name '{branch}'."
},
"Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}": {
"en": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"bg": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"de": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"ru": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"zh": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}"
},
"Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}": {
"en": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}",
"bg": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}",
"de": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}",
"ru": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}",
"zh": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}"
},
"Oops! Package build failed:\n{stderr}": {
"en": "Oops! Package build failed:\n{stderr}",
"bg": "Опа! Сборката на пакета неуспешна:\n{stderr}",
"de": "Ups! Paket-Build fehlgeschlagen:\n{stderr}",
"ru": "Ой! Сборка пакета не удалась:\n{stderr}",
"zh": "哎呀!包构建失败:\n{stderr}"
},
"Oops! PyPI publish failed:\n{stderr}": {
"en": "Oops! PyPI publish failed:\n{stderr}",
"bg": "Опа! Публикуването в PyPI неуспешно:\n{stderr}",
"de": "Ups! PyPI-Veröffentlichung fehlgeschlagen:\n{stderr}",
"ru": "Ой! Публикация в PyPI не удалась:\n{stderr}",
"zh": "哎呀!PyPI 发布失败:\n{stderr}"
},
"PASSED: {pair}": {
"en": "PASSED: {pair}",
"bg": "PASSED: {pair}",
"de": "PASSED: {pair}",
"ru": "PASSED: {pair}",
"zh": "PASSED: {pair}"
},
"PR number must be an integer, got: {pr_number}": {
"en": "PR number must be an integer, got: {pr_number}",
"bg": "PR number must be an integer, got: {pr_number}",
"de": "PR number must be an integer, got: {pr_number}",
"ru": "PR number must be an integer, got: {pr_number}",
"zh": "PR number must be an integer, got: {pr_number}"
},
"PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}": {
"en": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}",
"bg": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}",
"de": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}",
"ru": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}",
"zh": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}"
},
"PYPI_TOKEN not set and no registry URL configured — skipping PyPI publish. No worries, we'll just create the Gitea release.": {
"en": "PYPI_TOKEN not set and no registry URL configured — skipping PyPI publish. No worries, we'll just create the Gitea release.",
"bg": "PYPI_TOKEN не е зададен и няма конфигуриран URL на registry — пропускаме публикуването в PyPI. Без притеснения, просто ще създадем Gitea release.",
"de": "PYPI_TOKEN nicht gesetzt und keine Registry-URL konfiguriert — PyPI-Veröffentlichung wird übersprungen. Keine Sorge, wir erstellen einfach das Gitea-Release.",
"ru": "PYPI_TOKEN не задан и URL registry не настроен — пропускаем публикацию в PyPI. Не беспокойтесь, мы просто создадим Gitea release.",
"zh": "未设置 PYPI_TOKEN 且未配置 registry URL — 跳过 PyPI 发布。别担心,我们直接创建 Gitea release。"
},
"Published to Gitea PyPI registry.": {
"en": "Published to Gitea PyPI registry.",
"bg": "Публикувано в Gitea PyPI registry.",
"de": "In der Gitea PyPI-Registry veröffentlicht.",
"ru": "Опубликовано в Gitea PyPI registry.",
"zh": "已发布到 Gitea PyPI registry。"
},
"Published to PyPI.": {
"en": "Published to PyPI.",
"bg": "Публикувано в PyPI.",
"de": "In PyPI veröffentlicht.",
"ru": "Опубликовано в PyPI.",
"zh": "已发布到 PyPI。"
},
"Pushed release commit to master.": {
"en": "Pushed release commit to master.",
"bg": "Pushed release commit to master.",
"de": "Pushed release commit to master.",
"ru": "Pushed release commit to master.",
"zh": "Pushed release commit to master."
},
"Rebased and pushed. Retrying merge...": {
"en": "Rebased and pushed. Retrying merge...",
"bg": "Rebased and pushed. Retrying merge...",
"de": "Rebased and pushed. Retrying merge...",
"ru": "Rebased and pushed. Retrying merge...",
"zh": "Rebased and pushed. Retrying merge..."
},
"Release creation failed: {error}": {
"en": "Release creation failed: {error}",
"bg": "Release creation failed: {error}",
"de": "Release creation failed: {error}",
"ru": "Release creation failed: {error}",
"zh": "Release creation failed: {error}"
},
"Release must be run on master, currently on '{branch}'.": {
"en": "Release must be run on master, currently on '{branch}'.",
"bg": "Release must be run on master, currently on '{branch}'.",
"de": "Release must be run on master, currently on '{branch}'.",
"ru": "Release must be run on master, currently on '{branch}'.",
"zh": "Release must be run on master, currently on '{branch}'."
},
"Repo must be in 'owner/name' format, got: {repo}": {
"en": "Repo must be in 'owner/name' format, got: {repo}",
"bg": "Repo must be in 'owner/name' format, got: {repo}",
"de": "Repo must be in 'owner/name' format, got: {repo}",
"ru": "Repo must be in 'owner/name' format, got: {repo}",
"zh": "Repo must be in 'owner/name' format, got: {repo}"
},
"Repository configuration complete.": {
"en": "Repository configuration complete.",
"bg": "Конфигурирането на хранилището е завършено.",
"de": "Repository-Konfiguration abgeschlossen.",
"ru": "Конфигурация репозитория завершена.",
"zh": "仓库配置完成。"
},
"Runner index {index} out of range (0..{max})": {
"en": "Runner index {index} out of range (0..{max})",
"bg": "Индексът на runner {index} е извън диапазона (0..{max})",
"de": "Runner-Index {index} außerhalb des Bereichs (0..{max})",
"ru": "Индекс runner {index} вне диапазона (0..{max})",
"zh": "Runner 索引 {index} 超出范围 (0..{max})"
},
"Running lint checks...": {
"en": "Running lint checks...",
"bg": "Running lint checks...",
"de": "Running lint checks...",
"ru": "Running lint checks...",
"zh": "Running lint checks..."
},
"Running tests...": {
"en": "Running tests...",
"bg": "Running tests...",
"de": "Running tests...",
"ru": "Running tests...",
"zh": "Running tests..."
},
"Running: {scenario} on {platform}": {
"en": "Running: {scenario} on {platform}",
"bg": "Running: {scenario} on {platform}",
"de": "Running: {scenario} on {platform}",
"ru": "Running: {scenario} on {platform}",
"zh": "Running: {scenario} on {platform}"
},
"Skipping commit push — no staged changes.": {
"en": "Skipping commit push — no staged changes.",
"bg": "Skipping commit push — no staged changes.",
"de": "Skipping commit push — no staged changes.",
"ru": "Skipping commit push — no staged changes.",
"zh": "Skipping commit push — no staged changes."
},
"Syncing {count} documentation pages to wiki...": {
"en": "Syncing {count} documentation pages to wiki...",
"bg": "Syncing {count} documentation pages to wiki...",
"de": "Syncing {count} documentation pages to wiki...",
"ru": "Syncing {count} documentation pages to wiki...",
"zh": "Syncing {count} documentation pages to wiki..."
},
"Tag consistency check failed.": {
"en": "Tag consistency check failed.",
"bg": "Tag consistency check failed.",
"de": "Tag consistency check failed.",
"ru": "Tag consistency check failed.",
"zh": "Tag consistency check failed."
},
"Tag v{version} already existed. Publish workflow should already have been triggered.": {
"en": "Tag v{version} already existed. Publish workflow should already have been triggered.",
"bg": "Tag v{version} already existed. Publish workflow should already have been triggered.",
"de": "Tag v{version} already existed. Publish workflow should already have been triggered.",
"ru": "Tag v{version} already existed. Publish workflow should already have been triggered.",
"zh": "Tag v{version} already existed. Publish workflow should already have been triggered."
},
"Tag {tag} already exists and points to HEAD. Skipping creation.": {
"en": "Tag {tag} already exists and points to HEAD. Skipping creation.",
"bg": "Tag {tag} already exists and points to HEAD. Skipping creation.",
"de": "Tag {tag} already exists and points to HEAD. Skipping creation.",
"ru": "Tag {tag} already exists and points to HEAD. Skipping creation.",
"zh": "Tag {tag} already exists and points to HEAD. Skipping creation."
},
"Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.": {
"en": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.",
"bg": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.",
"de": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.",
"ru": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.",
"zh": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details."
},
"Task ID: {task_id}": {
"en": "Task ID: {task_id}",
"bg": "Task ID: {task_id}",
"de": "Task ID: {task_id}",
"ru": "Task ID: {task_id}",
"zh": "Task ID: {task_id}"
},
"Tests failed — refusing to release. Fix test failures first.\n{stderr}": {
"en": "Tests failed — refusing to release. Fix test failures first.\n{stderr}",
"bg": "Tests failed — refusing to release. Fix test failures first.\n{stderr}",
"de": "Tests failed — refusing to release. Fix test failures first.\n{stderr}",
"ru": "Tests failed — refusing to release. Fix test failures first.\n{stderr}",
"zh": "Tests failed — refusing to release. Fix test failures first.\n{stderr}"
},
"Tests passed.": {
"en": "Tests passed.",
"bg": "Tests passed.",
"de": "Tests passed.",
"ru": "Tests passed.",
"zh": "Tests passed."
},
"Unit tests passed in {duration:.2f}s (under {max}s limit).": {
"en": "Unit tests passed in {duration:.2f}s (under {max}s limit).",
"bg": "Unit tests passed in {duration:.2f}s (under {max}s limit).",
"de": "Unit tests passed in {duration:.2f}s (under {max}s limit).",
"ru": "Unit tests passed in {duration:.2f}s (under {max}s limit).",
"zh": "Unit tests passed in {duration:.2f}s (under {max}s limit)."
},
"Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.": {
"en": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
"bg": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
"de": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
"ru": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
"zh": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures."
},
"Unknown check category '{check}'. Available: all, user-facing{tags}": {
"en": "Unknown check category '{check}'. Available: all, user-facing{tags}",
"bg": "Unknown check category '{check}'. Available: all, user-facing{tags}",
"de": "Unknown check category '{check}'. Available: all, user-facing{tags}",
"ru": "Unknown check category '{check}'. Available: all, user-facing{tags}",
"zh": "Unknown check category '{check}'. Available: all, user-facing{tags}"
},
"Updated version in {init}": {
"en": "Updated version in {init}",
"bg": "Updated version in {init}",
"de": "Updated version in {init}",
"ru": "Updated version in {init}",
"zh": "Updated version in {init}"
},
"Updated {changelog_file}": {
"en": "Updated {changelog_file}",
"bg": "Updated {changelog_file}",
"de": "Updated {changelog_file}",
"ru": "Updated {changelog_file}",
"zh": "Updated {changelog_file}"
},
"VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.": {
"en": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.",
"bg": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.",
"de": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.",
"ru": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.",
"zh": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles."
},
"Version file: {file}": {
"en": "Version file: {file}",
"bg": "Version file: {file}",
"de": "Version file: {file}",
"ru": "Version file: {file}",
"zh": "Version file: {file}"
},
"Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.": {
"en": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.",
"bg": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.",
"de": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.",
"ru": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.",
"zh": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update."
},
"WARNING: --skip-tests passed — skipping test verification.": {
"en": "WARNING: --skip-tests passed — skipping test verification.",
"bg": "WARNING: --skip-tests passed — skipping test verification.",
"de": "WARNING: --skip-tests passed — skipping test verification.",
"ru": "WARNING: --skip-tests passed — skipping test verification.",
"zh": "WARNING: --skip-tests passed — skipping test verification."
},
"Warning: could not fetch tags from origin.": {
"en": "Warning: could not fetch tags from origin.",
"bg": "Warning: could not fetch tags from origin.",
"de": "Warning: could not fetch tags from origin.",
"ru": "Warning: could not fetch tags from origin.",
"zh": "Warning: could not fetch tags from origin."
},
"Wiki integrity check failed — {count} issue(s)": {
"en": "Wiki integrity check failed — {count} issue(s)",
"bg": "Wiki integrity check failed — {count} issue(s)",
"de": "Wiki integrity check failed — {count} issue(s)",
"ru": "Wiki integrity check failed — {count} issue(s)",
"zh": "Wiki integrity check failed — {count} issue(s)"
},
"Wiki verification failed — {failures} page(s) empty or mismatched": {
"en": "Wiki verification failed — {failures} page(s) empty or mismatched",
"bg": "Wiki verification failed — {failures} page(s) empty or mismatched",
"de": "Wiki verification failed — {failures} page(s) empty or mismatched",
"ru": "Wiki verification failed — {failures} page(s) empty or mismatched",
"zh": "Wiki verification failed — {failures} page(s) empty or mismatched"
},
"[dry-run] Would commit: release: v{version}": {
"en": "[dry-run] Would commit: release: v{version}",
"bg": "[dry-run] Would commit: release: v{version}",
"de": "[dry-run] Would commit: release: v{version}",
"ru": "[dry-run] Would commit: release: v{version}",
"zh": "[dry-run] Would commit: release: v{version}"
},
"[dry-run] Would create tag: v{version}": {
"en": "[dry-run] Would create tag: v{version}",
"bg": "[dry-run] Would create tag: v{version}",
"de": "[dry-run] Would create tag: v{version}",
"ru": "[dry-run] Would create tag: v{version}",
"zh": "[dry-run] Would create tag: v{version}"
},
"[dry-run] Would create tag: {tag}": {
"en": "[dry-run] Would create tag: {tag}",
"bg": "[dry-run] Would create tag: {tag}",
"de": "[dry-run] Would create tag: {tag}",
"ru": "[dry-run] Would create tag: {tag}",
"zh": "[dry-run] Would create tag: {tag}"
},
"[dry-run] Would push commit to master": {
"en": "[dry-run] Would push commit to master",
"bg": "[dry-run] Would push commit to master",
"de": "[dry-run] Would push commit to master",
"ru": "[dry-run] Would push commit to master",
"zh": "[dry-run] Would push commit to master"
},
"[dry-run] Would sync page: {title} ({chars} chars)": {
"en": "[dry-run] Would sync page: {title} ({chars} chars)",
"bg": "[dry-run] Would sync page: {title} ({chars} chars)",
"de": "[dry-run] Would sync page: {title} ({chars} chars)",
"ru": "[dry-run] Would sync page: {title} ({chars} chars)",
"zh": "[dry-run] Would sync page: {title} ({chars} chars)"
},
"[dry-run] Would update {changelog_file}": {
"en": "[dry-run] Would update {changelog_file}",
"bg": "[dry-run] Would update {changelog_file}",
"de": "[dry-run] Would update {changelog_file}",
"ru": "[dry-run] Would update {changelog_file}",
"zh": "[dry-run] Would update {changelog_file}"
},
"[dry-run] Would update {init}": {
"en": "[dry-run] Would update {init}",
"bg": "[dry-run] Would update {init}",
"de": "[dry-run] Would update {init}",
"ru": "[dry-run] Would update {init}",
"zh": "[dry-run] Would update {init}"
},
"active": {
"en": "active",
"bg": "активен",
"de": "aktiv",
"ru": "активен",
"zh": "活跃"
},
"completed": {
"en": "completed",
"bg": "завършен",
"de": "abgeschlossen",
"ru": "завершён",
"zh": "已完成"
},
"failed": {
"en": "failed",
"bg": "неуспешен",
"de": "fehlgeschlagen",
"ru": "неудачный",
"zh": "失败"
},
"git command failed ({cmd}): {stderr}": {
"en": "git command failed ({cmd}): {stderr}",
"bg": "git command failed ({cmd}): {stderr}",
"de": "git command failed ({cmd}): {stderr}",
"ru": "git command failed ({cmd}): {stderr}",
"zh": "git command failed ({cmd}): {stderr}"
},
"git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.": {
"en": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.",
"bg": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.",
"de": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.",
"ru": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.",
"zh": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history."
},
"git-cliff returned empty version.": {
"en": "git-cliff returned empty version.",
"bg": "git-cliff returned empty version.",
"de": "git-cliff returned empty version.",
"ru": "git-cliff returned empty version.",
"zh": "git-cliff returned empty version."
},
"git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).": {
"en": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).",
"bg": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).",
"de": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).",
"ru": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).",
"zh": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1)."
},
"in_progress": {
"en": "in progress",
"bg": "в процес",
"de": "in Bearbeitung",
"ru": "в процессе",
"zh": "进行中"
},
"inactive": {
"en": "inactive",
"bg": "неактивен",
"de": "inaktiv",
"ru": "неактивен",
"zh": "未激活"
},
"mapping.json keys and values must be strings, got {k}={v}": {
"en": "mapping.json keys and values must be strings, got {k}={v}",
"bg": "mapping.json keys and values must be strings, got {k}={v}",
"de": "mapping.json keys and values must be strings, got {k}={v}",
"ru": "mapping.json keys and values must be strings, got {k}={v}",
"zh": "mapping.json keys and values must be strings, got {k}={v}"
},
"mapping.json must be a dict of file-path -> page-title, got {type}": {
"en": "mapping.json must be a dict of file-path -> page-title, got {type}",
"bg": "mapping.json must be a dict of file-path -> page-title, got {type}",
"de": "mapping.json must be a dict of file-path -> page-title, got {type}",
"ru": "mapping.json must be a dict of file-path -> page-title, got {type}",
"zh": "mapping.json must be a dict of file-path -> page-title, got {type}"
},
"pending": {
"en": "pending",
"bg": "в очакване",
"de": "ausstehend",
"ru": "ожидает",
"zh": "待处理"
},
"unknown": {
"en": "unknown",
"bg": "неизвестен",
"de": "unbekannt",
"ru": "неизвестно",
"zh": "未知"
},
"{file} already exists. Use --force to overwrite.": {
"en": "{file} already exists. Use --force to overwrite.",
"bg": "{file} already exists. Use --force to overwrite.",
"de": "{file} already exists. Use --force to overwrite.",
"ru": "{file} already exists. Use --force to overwrite.",
"zh": "{file} already exists. Use --force to overwrite."
},
"--skip-build: skipping package build and PyPI publish.": {
"en": "--skip-build: skipping package build and PyPI publish.",
"bg": "--skip-build: skipping package build and PyPI publish.",
"de": "--skip-build: skipping package build and PyPI publish.",
"ru": "--skip-build: skipping package build and PyPI publish.",
"zh": "--skip-build: skipping package build and PyPI publish."
},
"Integration tests cancelled — another runner failed.": {
"en": "Integration tests cancelled — another runner failed.",
"bg": "Integration tests cancelled — another runner failed.",
"de": "Integration tests cancelled — another runner failed.",
"ru": "Integration tests cancelled — another runner failed.",
"zh": "Integration tests cancelled — another runner failed."
},
"Integration tests failed with exit code {code}": {
"en": "Integration tests failed with exit code {code}",
"bg": "Integration tests failed with exit code {code}",
"de": "Integration tests failed with exit code {code}",
"ru": "Integration tests failed with exit code {code}",
"zh": "Integration tests failed with exit code {code}"
},
"Integration tests passed.": {
"en": "Integration tests passed.",
"bg": "Integration tests passed.",
"de": "Integration tests passed.",
"ru": "Integration tests passed.",
"zh": "Integration tests passed."
},
"Merged {count} reports: {tests} tests, {failures} failures → {output}": {
"en": "Merged {count} reports: {tests} tests, {failures} failures → {output}",
"bg": "Merged {count} reports: {tests} tests, {failures} failures → {output}",
"de": "Merged {count} reports: {tests} tests, {failures} failures → {output}",
"ru": "Merged {count} reports: {tests} tests, {failures} failures → {output}",
"zh": "Merged {count} reports: {tests} tests, {failures} failures → {output}"
},
"No JUnit reports found matching {pattern} — skipping merge.": {
"en": "No JUnit reports found matching {pattern} — skipping merge.",
"bg": "No JUnit reports found matching {pattern} — skipping merge.",
"de": "No JUnit reports found matching {pattern} — skipping merge.",
"ru": "No JUnit reports found matching {pattern} — skipping merge.",
"zh": "No JUnit reports found matching {pattern} — skipping merge."
},
"Roles directory not found: {path}": {
"en": "Roles directory not found: {path}",
"bg": "Roles directory not found: {path}",
"de": "Roles directory not found: {path}",
"ru": "Roles directory not found: {path}",
"zh": "Roles directory not found: {path}"
}
"\n=== Summary ===": {
"en": "\n=== Summary ===",
"bg": "\n=== Summary ===",
"de": "\n=== Summary ===",
"ru": "\n=== Summary ===",
"zh": "\n=== Summary ==="
},
"\nAll documentation coverage checks passed!": {
"en": "\nAll documentation coverage checks passed!",
"bg": "\nAll documentation coverage checks passed!",
"de": "\nAll documentation coverage checks passed!",
"ru": "\nAll documentation coverage checks passed!",
"zh": "\nAll documentation coverage checks passed!"
},
"\nCHANGELOG version ordering:": {
"en": "\nCHANGELOG version ordering:",
"bg": "\nCHANGELOG version ordering:",
"de": "\nCHANGELOG version ordering:",
"ru": "\nCHANGELOG version ordering:",
"zh": "\nCHANGELOG version ordering:"
},
"\nChecking CI script documentation in ci-cd-workflow.md...": {
"en": "\nChecking CI script documentation in ci-cd-workflow.md...",
"bg": "\nChecking CI script documentation in ci-cd-workflow.md...",
"de": "\nChecking CI script documentation in ci-cd-workflow.md...",
"ru": "\nChecking CI script documentation in ci-cd-workflow.md...",
"zh": "\nChecking CI script documentation in ci-cd-workflow.md..."
},
"\nChecking module documentation in architecture.md...": {
"en": "\nChecking module documentation in architecture.md...",
"bg": "\nChecking module documentation in architecture.md...",
"de": "\nChecking module documentation in architecture.md...",
"ru": "\nChecking module documentation in architecture.md...",
"zh": "\nChecking module documentation in architecture.md..."
},
"\nDoc coverage: {covered}/{total} ({pct}%)": {
"en": "\nDoc coverage: {covered}/{total} ({pct}%)",
"bg": "\nDoc coverage: {covered}/{total} ({pct}%)",
"de": "\nDoc coverage: {covered}/{total} ({pct}%)",
"ru": "\nDoc coverage: {covered}/{total} ({pct}%)",
"zh": "\nDoc coverage: {covered}/{total} ({pct}%)"
},
"\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}": {
"en": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
"bg": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
"de": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
"ru": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
"zh": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}"
},
"\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.": {
"en": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.",
"bg": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.",
"de": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.",
"ru": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.",
"zh": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce."
},
"\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.": {
"en": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.",
"bg": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.",
"de": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.",
"ru": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.",
"zh": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report."
},
"\nIntegrity check FAILED ({count} issues):": {
"en": "\nIntegrity check FAILED ({count} issues):",
"bg": "\nIntegrity check FAILED ({count} issues):",
"de": "\nIntegrity check FAILED ({count} issues):",
"ru": "\nIntegrity check FAILED ({count} issues):",
"zh": "\nIntegrity check FAILED ({count} issues):"
},
"\nIntegrity check passed — all {count} pages verified.": {
"en": "\nIntegrity check passed — all {count} pages verified.",
"bg": "\nIntegrity check passed — all {count} pages verified.",
"de": "\nIntegrity check passed — all {count} pages verified.",
"ru": "\nIntegrity check passed — all {count} pages verified.",
"zh": "\nIntegrity check passed — all {count} pages verified."
},
"\nLatest tag: {tag}": {
"en": "\nLatest tag: {tag}",
"bg": "\nLatest tag: {tag}",
"de": "\nLatest tag: {tag}",
"ru": "\nLatest tag: {tag}",
"zh": "\nLatest tag: {tag}"
},
"\nMissing documentation:": {
"en": "\nMissing documentation:",
"bg": "\nMissing documentation:",
"de": "\nMissing documentation:",
"ru": "\nMissing documentation:",
"zh": "\nMissing documentation:"
},
"\nResult: {status}": {
"en": "\nResult: {status}",
"bg": "\nResult: {status}",
"de": "\nResult: {status}",
"ru": "\nResult: {status}",
"zh": "\nResult: {status}"
},
"\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).": {
"en": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).",
"bg": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).",
"de": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).",
"ru": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).",
"zh": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments)."
},
"\nRunning full wiki integrity check...": {
"en": "\nRunning full wiki integrity check...",
"bg": "\nRunning full wiki integrity check...",
"de": "\nRunning full wiki integrity check...",
"ru": "\nRunning full wiki integrity check...",
"zh": "\nRunning full wiki integrity check..."
},
"\nTag → Commit alignment:": {
"en": "\nTag → Commit alignment:",
"bg": "\nTag → Commit alignment:",
"de": "\nTag → Commit alignment:",
"ru": "\nTag → Commit alignment:",
"zh": "\nTag → Commit alignment:"
},
"\nUntagged release commits:": {
"en": "\nUntagged release commits:",
"bg": "\nUntagged release commits:",
"de": "\nUntagged release commits:",
"ru": "\nUntagged release commits:",
"zh": "\nUntagged release commits:"
},
"\nUser-facing changes ({count}):": {
"en": "\nUser-facing changes ({count}):",
"bg": "\nUser-facing changes ({count}):",
"de": "\nUser-facing changes ({count}):",
"ru": "\nUser-facing changes ({count}):",
"zh": "\nUser-facing changes ({count}):"
},
"\nVerification FAILED: {failures} page(s) have empty or mismatched content!": {
"en": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
"bg": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
"de": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
"ru": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
"zh": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!"
},
"\nVerification passed — all wiki pages have correct content.": {
"en": "\nVerification passed — all wiki pages have correct content.",
"bg": "\nVerification passed — all wiki pages have correct content.",
"de": "\nVerification passed — all wiki pages have correct content.",
"ru": "\nVerification passed — all wiki pages have correct content.",
"zh": "\nVerification passed — all wiki pages have correct content."
},
"\nVerifying wiki pages have content...": {
"en": "\nVerifying wiki pages have content...",
"bg": "\nVerifying wiki pages have content...",
"de": "\nVerifying wiki pages have content...",
"ru": "\nVerifying wiki pages have content...",
"zh": "\nVerifying wiki pages have content..."
},
"\nWorkflow-only changes ({count}):": {
"en": "\nWorkflow-only changes ({count}):",
"bg": "\nWorkflow-only changes ({count}):",
"de": "\nWorkflow-only changes ({count}):",
"ru": "\nWorkflow-only changes ({count}):",
"zh": "\nWorkflow-only changes ({count}):"
},
"\n[dry-run] Changelog:\n{changelog}": {
"en": "\n[dry-run] Changelog:\n{changelog}",
"bg": "\n[dry-run] Changelog:\n{changelog}",
"de": "\n[dry-run] Changelog:\n{changelog}",
"ru": "\n[dry-run] Changelog:\n{changelog}",
"zh": "\n[dry-run] Changelog:\n{changelog}"
},
"\n{label} files changed ({count}):": {
"en": "\n{label} files changed ({count}):",
"bg": "\n{label} files changed ({count}):",
"de": "\n{label} files changed ({count}):",
"ru": "\n{label} files changed ({count}):",
"zh": "\n{label} files changed ({count}):"
},
"\n{tag} files ({count}):": {
"en": "\n{tag} files ({count}):",
"bg": "\n{tag} files ({count}):",
"de": "\n{tag} files ({count}):",
"ru": "\n{tag} files ({count}):",
"zh": "\n{tag} files ({count}):"
},
" - Auto-delete branch after merge: yes": {
"en": " - Auto-delete branch after merge: yes",
"bg": " - Автоматично изтриване на клон след сливане: да",
"de": " - Branch nach Merge automatisch löschen: ja",
"ru": " - Автоудаление ветки после слияния: да",
"zh": " - 合并后自动删除分支: 是"
},
" - Block outdated branches: yes": {
"en": " - Block outdated branches: yes",
"bg": " - Блокиране на остарели клонове: да",
"de": " - Veraltete Branches blockieren: ja",
"ru": " - Блокировать устаревшие ветки: да",
"zh": " - 阻止过时分支: 是"
},
" - Block rejected reviews: yes": {
"en": " - Block rejected reviews: yes",
"bg": " - Блокиране на отхвърлени рецензии: да",
"de": " - Abgelehnte Reviews blockieren: ja",
"ru": " - Блокировать отклонённые ревью: да",
"zh": " - 阻止被拒绝的审查: 是"
},
" - Direct pushes: BLOCKED (require PR, whitelisted users can push)": {
"en": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)",
"bg": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)",
"de": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)",
"ru": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)",
"zh": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)"
},
" - Dismiss stale approvals: yes": {
"en": " - Dismiss stale approvals: yes",
"bg": " - Анулиране на остарели одобрения: да",
"de": " - Veraltete Genehmigungen ablehnen: ja",
"ru": " - Отклонять устаревшие одобрения: да",
"zh": " - 忽略过时审批: 是"
},
" - Required approvals: {count}": {
"en": " - Required approvals: {count}",
"bg": " - Необходими одобрения: {count}",
"de": " - Erforderliche Genehmigungen: {count}",
"ru": " - Требуемые одобрения: {count}",
"zh": " - 必需审批数: {count}"
},
" - Required status checks: {checks}": {
"en": " - Required status checks: {checks}",
"bg": " - Необходими проверки на състоянието: {checks}",
"de": " - Erforderliche Status-Checks: {checks}",
"ru": " - Требуемые проверки статуса: {checks}",
"zh": " - 必需状态检查: {checks}"
},
" Created: {title}": {
"en": " Created: {title}",
"bg": " Created: {title}",
"de": " Created: {title}",
"ru": " Created: {title}",
"zh": " Created: {title}"
},
" FAIL: {title} — content mismatch or empty!": {
"en": " FAIL: {title} — content mismatch or empty!",
"bg": " FAIL: {title} — content mismatch or empty!",
"de": " FAIL: {title} — content mismatch or empty!",
"ru": " FAIL: {title} — content mismatch or empty!",
"zh": " FAIL: {title} — content mismatch or empty!"
},
" MISSING: devx {cmd}": {
"en": " MISSING: devx {cmd}",
"bg": " ЛИПСВА: devx {cmd}",
"de": " FEHLT: devx {cmd}",
"ru": " ОТСУТСТВУЕТ: devx {cmd}",
"zh": " 缺失: devx {cmd}"
},
" MISSING: {module}": {
"en": " MISSING: {module}",
"bg": " MISSING: {module}",
"de": " MISSING: {module}",
"ru": " MISSING: {module}",
"zh": " MISSING: {module}"
},
" MISSING: {script}": {
"en": " MISSING: {script}",
"bg": " MISSING: {script}",
"de": " MISSING: {script}",
"ru": " MISSING: {script}",
"zh": " MISSING: {script}"
},
" OK: devx {cmd}": {
"en": " OK: devx {cmd}",
"bg": " ОК: devx {cmd}",
"de": " OK: devx {cmd}",
"ru": " ОК: devx {cmd}",
"zh": " 正常: devx {cmd}"
},
" OK: {module}": {
"en": " OK: {module}",
"bg": " OK: {module}",
"de": " OK: {module}",
"ru": " OK: {module}",
"zh": " OK: {module}"
},
" OK: {script}": {
"en": " OK: {script}",
"bg": " OK: {script}",
"de": " OK: {script}",
"ru": " OK: {script}",
"zh": " OK: {script}"
},
" OK: {title} ({chars} chars)": {
"en": " OK: {title} ({chars} chars)",
"bg": " OK: {title} ({chars} chars)",
"de": " OK: {title} ({chars} chars)",
"ru": " OK: {title} ({chars} chars)",
"zh": " OK: {title} ({chars} chars)"
},
" Updated: {title}": {
"en": " Updated: {title}",
"bg": " Updated: {title}",
"de": " Updated: {title}",
"ru": " Updated: {title}",
"zh": " Updated: {title}"
},
"=== Release Alignment Verification ===\n": {
"en": "=== Release Alignment Verification ===\n",
"bg": "=== Release Alignment Verification ===\n",
"de": "=== Release Alignment Verification ===\n",
"ru": "=== Release Alignment Verification ===\n",
"zh": "=== Release Alignment Verification ===\n"
},
"API poll warning: {exc}": {
"en": "API poll warning: {exc}",
"bg": "API poll warning: {exc}",
"de": "API poll warning: {exc}",
"ru": "API poll warning: {exc}",
"zh": "API poll warning: {exc}"
},
"All molecule tests passed.": {
"en": "All molecule tests passed.",
"bg": "All molecule tests passed.",
"de": "All molecule tests passed.",
"ru": "All molecule tests passed.",
"zh": "All molecule tests passed."
},
"Another molecule runner failed. Stopping this runner early.": {
"en": "Another molecule runner failed. Stopping this runner early.",
"bg": "Another molecule runner failed. Stopping this runner early.",
"de": "Another molecule runner failed. Stopping this runner early.",
"ru": "Another molecule runner failed. Stopping this runner early.",
"zh": "Another molecule runner failed. Stopping this runner early."
},
"Bumping version: {current} -> v{new_version}": {
"en": "Bumping version: {current} -> v{new_version}",
"bg": "Bumping version: {current} -> v{new_version}",
"de": "Bumping version: {current} -> v{new_version}",
"ru": "Bumping version: {current} -> v{new_version}",
"zh": "Bumping version: {current} -> v{new_version}"
},
"Checking CLI command documentation...": {
"en": "Checking CLI command documentation...",
"bg": "Checking CLI command documentation...",
"de": "Checking CLI command documentation...",
"ru": "Checking CLI command documentation...",
"zh": "Checking CLI command documentation..."
},
"Command failed ({cmd}): {stderr}": {
"en": "Command failed ({cmd}): {stderr}",
"bg": "Command failed ({cmd}): {stderr}",
"de": "Command failed ({cmd}): {stderr}",
"ru": "Command failed ({cmd}): {stderr}",
"zh": "Command failed ({cmd}): {stderr}"
},
"Comparing {base}..{head} ({count} files changed)": {
"en": "Comparing {base}..{head} ({count} files changed)",
"bg": "Comparing {base}..{head} ({count} files changed)",
"de": "Comparing {base}..{head} ({count} files changed)",
"ru": "Comparing {base}..{head} ({count} files changed)",
"zh": "Comparing {base}..{head} ({count} files changed)"
},
"Configuring branch protection for {branch}...": {
"en": "Configuring branch protection for {branch}...",
"bg": "Конфигуриране на защита на клона {branch}...",
"de": "Konfiguriere Branch-Schutz für {branch}...",
"ru": "Настройка защиты ветки {branch}...",
"zh": "正在配置 {branch} 的分支保护..."
},
"Configuring repository settings...": {
"en": "Configuring repository settings...",
"bg": "Конфигуриране на настройките на хранилището...",
"de": "Repository-Einstellungen konfigurieren...",
"ru": "Настройка параметров репозитория...",
"zh": "正在配置仓库设置..."
},
"Could not extract conventional commit message from PR commits.": {
"en": "Could not extract conventional commit message from PR commits.",
"bg": "Could not extract conventional commit message from PR commits.",
"de": "Could not extract conventional commit message from PR commits.",
"ru": "Could not extract conventional commit message from PR commits.",
"zh": "Could not extract conventional commit message from PR commits."
},
"Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.": {
"en": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.",
"bg": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.",
"de": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.",
"ru": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.",
"zh": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task."
},
"Could not find __version__ in {file}": {
"en": "Could not find __version__ in {file}",
"bg": "Could not find __version__ in {file}",
"de": "Could not find __version__ in {file}",
"ru": "Could not find __version__ in {file}",
"zh": "Could not find __version__ in {file}"
},
"Could not parse test execution time from output.": {
"en": "Could not parse test execution time from output.",
"bg": "Could not parse test execution time from output.",
"de": "Could not parse test execution time from output.",
"ru": "Could not parse test execution time from output.",
"zh": "Could not parse test execution time from output."
},
"Created issue #{issue_id}: {title}": {
"en": "Created issue #{issue_id}: {title}",
"bg": "Created issue #{issue_id}: {title}",
"de": "Created issue #{issue_id}: {title}",
"ru": "Created issue #{issue_id}: {title}",
"zh": "Created issue #{issue_id}: {title}"
},
"Created release commit.": {
"en": "Created release commit.",
"bg": "Created release commit.",
"de": "Created release commit.",
"ru": "Created release commit.",
"zh": "Created release commit."
},
"Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.": {
"en": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.",
"bg": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.",
"de": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.",
"ru": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.",
"zh": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently."
},
"ERROR: REPO_TOKEN is not set.": {
"en": "ERROR: REPO_TOKEN is not set.",
"bg": "ГРЕШКА: REPO_TOKEN не е зададен.",
"de": "FEHLER: REPO_TOKEN ist nicht gesetzt.",
"ru": "ОШИБКА: REPO_TOKEN не задан.",
"zh": "错误:未设置 REPO_TOKEN。"
},
"ERROR: Repository name not specified. Use --repo or set DEVX_REPO_NAME.": {
"en": "ERROR: Repository name not specified. Use --repo or set DEVX_REPO_NAME.",
"bg": "ГРЕШКА: Името на хранилището не е указано. Използвайте --repo или задайте DEVX_REPO_NAME.",
"de": "FEHLER: Repository-Name nicht angegeben. Verwenden Sie --repo oder setzen Sie DEVX_REPO_NAME.",
"ru": "ОШИБКА: Имя репозитория не указано. Используйте --repo или задайте DEVX_REPO_NAME.",
"zh": "错误:未指定仓库名称。请使用 --repo 或设置 DEVX_REPO_NAME。"
},
"ERROR: Tag consistency check failed. Existing tags are misaligned:": {
"en": "ERROR: Tag consistency check failed. Existing tags are misaligned:",
"bg": "ERROR: Tag consistency check failed. Existing tags are misaligned:",
"de": "ERROR: Tag consistency check failed. Existing tags are misaligned:",
"ru": "ERROR: Tag consistency check failed. Existing tags are misaligned:",
"zh": "ERROR: Tag consistency check failed. Existing tags are misaligned:"
},
"ERROR: VIKUNJA_TOKEN is not set.": {
"en": "ERROR: VIKUNJA_TOKEN is not set.",
"bg": "ГРЕШКА: VIKUNJA_TOKEN не е зададен.",
"de": "FEHLER: VIKUNJA_TOKEN ist nicht gesetzt.",
"ru": "ОШИБКА: VIKUNJA_TOKEN не задан.",
"zh": "错误:未设置 VIKUNJA_TOKEN。"
},
"ERROR: mapping.json not found at {path}": {
"en": "ERROR: mapping.json not found at {path}",
"bg": "ERROR: mapping.json not found at {path}",
"de": "ERROR: mapping.json not found at {path}",
"ru": "ERROR: mapping.json not found at {path}",
"zh": "ERROR: mapping.json not found at {path}"
},
"FAILED: {pair} exited with code {code}": {
"en": "FAILED: {pair} exited with code {code}",
"bg": "FAILED: {pair} exited with code {code}",
"de": "FAILED: {pair} exited with code {code}",
"ru": "FAILED: {pair} exited with code {code}",
"zh": "FAILED: {pair} exited with code {code}"
},
"Failed to create issue via tea: {error}": {
"en": "Failed to create issue via tea: {error}",
"bg": "Failed to create issue via tea: {error}",
"de": "Failed to create issue via tea: {error}",
"ru": "Failed to create issue via tea: {error}",
"zh": "Failed to create issue via tea: {error}"
},
"Found {count} existing wiki pages.": {
"en": "Found {count} existing wiki pages.",
"bg": "Found {count} existing wiki pages.",
"de": "Found {count} existing wiki pages.",
"ru": "Found {count} existing wiki pages.",
"zh": "Found {count} existing wiki pages."
},
"GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.": {
"en": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.",
"bg": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.",
"de": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.",
"ru": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.",
"zh": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation."
},
"Generated {file} with prefix '{prefix}'.": {
"en": "Generated {file} with prefix '{prefix}'.",
"bg": "Generated {file} with prefix '{prefix}'.",
"de": "Generated {file} with prefix '{prefix}'.",
"ru": "Generated {file} with prefix '{prefix}'.",
"zh": "Generated {file} with prefix '{prefix}'."
},
"HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.": {
"en": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
"bg": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
"de": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
"ru": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
"zh": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag."
},
"HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.": {
"en": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.",
"bg": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.",
"de": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.",
"ru": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.",
"zh": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment."
},
"HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.": {
"en": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.",
"bg": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.",
"de": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.",
"ru": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.",
"zh": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping."
},
"HTTP error: {status} — {message}": {
"en": "HTTP error: {status} — {message}",
"bg": "HTTP грешка: {status} — {message}",
"de": "HTTP-Fehler: {status} — {message}",
"ru": "Ошибка HTTP: {status} — {message}",
"zh": "HTTP 错误: {status} — {message}"
},
"HTTP {status} Forbidden — your token lacks admin rights.\nMake sure the token belongs to a repo owner or organisation admin.\nAlternatively, configure branch protection manually in Settings → Branches.": {
"en": "HTTP {status} Forbidden — your token lacks admin rights.\nMake sure the token belongs to a repo owner or organisation admin.\nAlternatively, configure branch protection manually in Settings → Branches.",
"bg": "HTTP {status} Забранено — вашият токен няма администраторски права.\nУверете се, че токенът принадлежи на собственик на хранилище или администратор на организация.\nАлтернативно, конфигурирайте защитата на клона ръчно в Настройки → Клонове.",
"de": "HTTP {status} Verboten — Ihr Token hat keine Admin-Rechte.\nStellen Sie sicher, dass das Token einem Repository-Besitzer oder Organisations-Admin gehört.\nAlternativ können Sie den Branch-Schutz manuell unter Einstellungen → Branches konfigurieren.",
"ru": "HTTP {status} Запрещено — у вашего токена нет прав администратора.\nУбедитесь, что токен принадлежит владельцу репозитория или администратору организации.\nЛибо настройте защиту ветки вручную в разделе Настройки → Ветки.",
"zh": "HTTP {status} 禁止访问 — 您的令牌缺少管理员权限。\n请确保令牌属于仓库所有者或组织管理员。\n或者,您可以在 设置 → 分支 中手动配置分支保护。"
},
"Head branch is behind master. Pulling and rebasing...": {
"en": "Head branch is behind master. Pulling and rebasing...",
"bg": "Head branch is behind master. Pulling and rebasing...",
"de": "Head branch is behind master. Pulling and rebasing...",
"ru": "Head branch is behind master. Pulling and rebasing...",
"zh": "Head branch is behind master. Pulling and rebasing..."
},
"Infrastructure commit (no DEVX-N task ID), skipping Vikunja update: {msg}": {
"en": "Infrastructure commit (no DEVX-N task ID), skipping Vikunja update: {msg}",
"bg": "Инфраструктурен commit (без идентификатор на задача DEVX-N), пропускаме обновяването на Vikunja: {msg}",
"de": "Infrastruktur-Commit (keine DEVX-N Task-ID), Vikunja-Update wird übersprungen: {msg}",
"ru": "Инфраструктурный коммит (без ID задачи DEVX-N), пропуск обновления Vikunja: {msg}",
"zh": "基础设施提交(无 DEVX-N 任务 ID),跳过 Vikunja 更新: {msg}"
},
"Lint failed — refusing to release. Fix lint errors first.\n{stderr}": {
"en": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}",
"bg": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}",
"de": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}",
"ru": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}",
"zh": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}"
},
"Lint passed.": {
"en": "Lint passed.",
"bg": "Lint passed.",
"de": "Lint passed.",
"ru": "Lint passed.",
"zh": "Lint passed."
},
"Mapped file {file} is empty. Update the content or remove from mapping.json.": {
"en": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
"bg": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
"de": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
"ru": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
"zh": "Mapped file {file} is empty. Update the content or remove from mapping.json."
},
"Mapped file {file} not found. Update mapping.json or create the file.": {
"en": "Mapped file {file} not found. Update mapping.json or create the file.",
"bg": "Mapped file {file} not found. Update mapping.json or create the file.",
"de": "Mapped file {file} not found. Update mapping.json or create the file.",
"ru": "Mapped file {file} not found. Update mapping.json or create the file.",
"zh": "Mapped file {file} not found. Update mapping.json or create the file."
},
"Merge failed after rebase retry: {error}\nPlease rebase the PR manually.": {
"en": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
"bg": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
"de": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
"ru": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
"zh": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually."
},
"Merge failed with HTTP {status}: {message}\nPlease check the PR is ready and you have merge rights.": {
"en": "Merge failed with HTTP {status}: {message}\nPlease check the PR is ready and you have merge rights.",
"bg": "Сливането неуспешно с HTTP {status}: {message}\nПроверете дали PR е готов и имате права за сливане.",
"de": "Merge fehlgeschlagen mit HTTP {status}: {message}\nBitte prüfen Sie, ob der PR bereit ist und Sie Merge-Rechte haben.",
"ru": "Слияние не удалось: HTTP {status}: {message}\nПроверьте, что PR готов и у вас есть права на слияние.",
"zh": "合并失败: HTTP {status}: {message}\n请检查 PR 是否准备就绪且您具有合并权限。"
},
"Module {mod} has no main() function": {
"en": "Module {mod} has no main() function",
"bg": "Модул {mod} няма функция main()",
"de": "Modul {mod} hat keine main()-Funktion",
"ru": "Модуль {mod} не имеет функции main()",
"zh": "模块 {mod} 没有 main() 函数"
},
"Molecule directory not found: {path}": {
"en": "Molecule directory not found: {path}",
"bg": "Директорията на molecule не е намерена: {path}",
"de": "Molecule-Verzeichnis nicht gefunden: {path}",
"ru": "Директория molecule не найдена: {path}",
"zh": "未找到 molecule 目录: {path}"
},
"Nice! Gitea release {tag} created.": {
"en": "Nice! Gitea release {tag} created.",
"bg": "Отлично! Gitea release {tag} е създаден.",
"de": "Prima! Gitea-Release {tag} erstellt.",
"ru": "Отлично! Gitea release {tag} создан.",
"zh": "不错!Gitea release {tag} 已创建。"
},
"Nice! PR #{pr_number} squash-merged with title: {merge_title}": {
"en": "Nice! PR #{pr_number} squash-merged with title: {merge_title}",
"bg": "Отлично! PR #{pr_number} е squash-merge-нат със заглавие: {merge_title}",
"de": "Prima! PR #{pr_number} wurde mit Titel {merge_title} squash-gemergt.",
"ru": "Отлично! PR #{pr_number} squash-merge с заголовком: {merge_title}",
"zh": "不错!PR #{pr_number} 已 squash 合并,标题: {merge_title}"
},
"Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.": {
"en": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.",
"bg": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.",
"de": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.",
"ru": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.",
"zh": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered."
},
"Nice! Vikunja task {task_id} (ID {vikunja_id}) updated and marked done.": {
"en": "Nice! Vikunja task {task_id} (ID {vikunja_id}) updated and marked done.",
"bg": "Отлично! Задача Vikunja {task_id} (ID {vikunja_id}) е обновена и маркирана като готова.",
"de": "Prima! Vikunja-Aufgabe {task_id} (ID {vikunja_id}) aktualisiert und als erledigt markiert.",
"ru": "Отлично! Задача Vikunja {task_id} (ID {vikunja_id}) обновлена и отмечена как выполненная.",
"zh": "不错!Vikunja 任务 {task_id} (ID {vikunja_id}) 已更新并标记为完成。"
},
"No changes between {base} and {head}.": {
"en": "No changes between {base} and {head}.",
"bg": "No changes between {base} and {head}.",
"de": "No changes between {base} and {head}.",
"ru": "No changes between {base} and {head}.",
"zh": "No changes between {base} and {head}."
},
"No staged changes — version and changelog already up to date.": {
"en": "No staged changes — version and changelog already up to date.",
"bg": "No staged changes — version and changelog already up to date.",
"de": "No staged changes — version and changelog already up to date.",
"ru": "No staged changes — version and changelog already up to date.",
"zh": "No staged changes — version and changelog already up to date."
},
"No tags found — treating all changes as user-facing.": {
"en": "No tags found — treating all changes as user-facing.",
"bg": "No tags found — treating all changes as user-facing.",
"de": "No tags found — treating all changes as user-facing.",
"ru": "No tags found — treating all changes as user-facing.",
"zh": "No tags found — treating all changes as user-facing."
},
"No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.": {
"en": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.",
"bg": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.",
"de": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.",
"ru": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.",
"zh": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID."
},
"No unreleased changes found. Nothing to release.": {
"en": "No unreleased changes found. Nothing to release.",
"bg": "No unreleased changes found. Nothing to release.",
"de": "No unreleased changes found. Nothing to release.",
"ru": "No unreleased changes found. Nothing to release.",
"zh": "No unreleased changes found. Nothing to release."
},
"No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.": {
"en": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.",
"bg": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.",
"de": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.",
"ru": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.",
"zh": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release."
},
"Note: Self-approval not allowed. Posting COMMENT instead.": {
"en": "Note: Self-approval not allowed. Posting COMMENT instead.",
"bg": "Note: Self-approval not allowed. Posting COMMENT instead.",
"de": "Note: Self-approval not allowed. Posting COMMENT instead.",
"ru": "Note: Self-approval not allowed. Posting COMMENT instead.",
"zh": "Note: Self-approval not allowed. Posting COMMENT instead."
},
"Oops! Commit message must follow conventional commit format.\n Expected: <type>: <description>\n Got: {subject}\n Allowed types: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE": {
"en": "Oops! Commit message must follow conventional commit format.\n Expected: <type>: <description>\n Got: {subject}\n Allowed types: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"bg": "Опа! Съобщението за commit трябва да следва конвенционален формат.\n Очаква се: <type>: <description>\n Получено: {subject}\n Разрешени типове: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"de": "Ups! Commit-Nachricht muss dem konventionellen Commit-Format folgen.\n Erwartet: <type>: <description>\n Erhalten: {subject}\n Erlaubte Typen: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"ru": "Ой! Сообщение коммита должно соответствовать формату conventional commit.\n Ожидается: <type>: <description>\n Получено: {subject}\n Допустимые типы: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"zh": "哎呀!提交消息必须遵循 conventional commit 格式。\n 预期格式: <type>: <description>\n 实际: {subject}\n 允许的类型: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE"
},
"Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.": {
"en": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.",
"bg": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.",
"de": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.",
"ru": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.",
"zh": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI."
},
"Oops! Gitea PyPI registry publish failed:\n{stderr}": {
"en": "Oops! Gitea PyPI registry publish failed:\n{stderr}",
"bg": "Опа! Публикуването в Gitea PyPI registry неуспешно:\n{stderr}",
"de": "Ups! Veröffentlichung in der Gitea PyPI-Registry fehlgeschlagen:\n{stderr}",
"ru": "Ой! Публикация в Gitea PyPI registry не удалась:\n{stderr}",
"zh": "哎呀!Gitea PyPI registry 发布失败:\n{stderr}"
},
"Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}": {
"en": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}",
"bg": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}",
"de": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}",
"ru": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}",
"zh": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}"
},
"Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}": {
"en": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"bg": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"de": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"ru": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"zh": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}"
},
"Oops! No task ID found in .taskid file or branch name '{branch}'.": {
"en": "Oops! No task ID found in .taskid file or branch name '{branch}'.",
"bg": "Oops! No task ID found in .taskid file or branch name '{branch}'.",
"de": "Oops! No task ID found in .taskid file or branch name '{branch}'.",
"ru": "Oops! No task ID found in .taskid file or branch name '{branch}'.",
"zh": "Oops! No task ID found in .taskid file or branch name '{branch}'."
},
"Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}": {
"en": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"bg": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"de": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"ru": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"zh": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}"
},
"Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}": {
"en": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}",
"bg": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}",
"de": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}",
"ru": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}",
"zh": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}"
},
"Oops! Package build failed:\n{stderr}": {
"en": "Oops! Package build failed:\n{stderr}",
"bg": "Опа! Сборката на пакета неуспешна:\n{stderr}",
"de": "Ups! Paket-Build fehlgeschlagen:\n{stderr}",
"ru": "Ой! Сборка пакета не удалась:\n{stderr}",
"zh": "哎呀!包构建失败:\n{stderr}"
},
"Oops! PyPI publish failed:\n{stderr}": {
"en": "Oops! PyPI publish failed:\n{stderr}",
"bg": "Опа! Публикуването в PyPI неуспешно:\n{stderr}",
"de": "Ups! PyPI-Veröffentlichung fehlgeschlagen:\n{stderr}",
"ru": "Ой! Публикация в PyPI не удалась:\n{stderr}",
"zh": "哎呀!PyPI 发布失败:\n{stderr}"
},
"PASSED: {pair}": {
"en": "PASSED: {pair}",
"bg": "PASSED: {pair}",
"de": "PASSED: {pair}",
"ru": "PASSED: {pair}",
"zh": "PASSED: {pair}"
},
"PR number must be an integer, got: {pr_number}": {
"en": "PR number must be an integer, got: {pr_number}",
"bg": "PR number must be an integer, got: {pr_number}",
"de": "PR number must be an integer, got: {pr_number}",
"ru": "PR number must be an integer, got: {pr_number}",
"zh": "PR number must be an integer, got: {pr_number}"
},
"PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}": {
"en": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}",
"bg": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}",
"de": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}",
"ru": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}",
"zh": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}"
},
"PYPI_TOKEN not set and no registry URL configured — skipping PyPI publish. No worries, we'll just create the Gitea release.": {
"en": "PYPI_TOKEN not set and no registry URL configured — skipping PyPI publish. No worries, we'll just create the Gitea release.",
"bg": "PYPI_TOKEN не е зададен и няма конфигуриран URL на registry — пропускаме публикуването в PyPI. Без притеснения, просто ще създадем Gitea release.",
"de": "PYPI_TOKEN nicht gesetzt und keine Registry-URL konfiguriert — PyPI-Veröffentlichung wird übersprungen. Keine Sorge, wir erstellen einfach das Gitea-Release.",
"ru": "PYPI_TOKEN не задан и URL registry не настроен — пропускаем публикацию в PyPI. Не беспокойтесь, мы просто создадим Gitea release.",
"zh": "未设置 PYPI_TOKEN 且未配置 registry URL — 跳过 PyPI 发布。别担心,我们直接创建 Gitea release。"
},
"Published to Gitea PyPI registry.": {
"en": "Published to Gitea PyPI registry.",
"bg": "Публикувано в Gitea PyPI registry.",
"de": "In der Gitea PyPI-Registry veröffentlicht.",
"ru": "Опубликовано в Gitea PyPI registry.",
"zh": "已发布到 Gitea PyPI registry。"
},
"Published to PyPI.": {
"en": "Published to PyPI.",
"bg": "Публикувано в PyPI.",
"de": "In PyPI veröffentlicht.",
"ru": "Опубликовано в PyPI.",
"zh": "已发布到 PyPI。"
},
"Pushed release commit to master.": {
"en": "Pushed release commit to master.",
"bg": "Pushed release commit to master.",
"de": "Pushed release commit to master.",
"ru": "Pushed release commit to master.",
"zh": "Pushed release commit to master."
},
"Rebased and pushed. Retrying merge...": {
"en": "Rebased and pushed. Retrying merge...",
"bg": "Rebased and pushed. Retrying merge...",
"de": "Rebased and pushed. Retrying merge...",
"ru": "Rebased and pushed. Retrying merge...",
"zh": "Rebased and pushed. Retrying merge..."
},
"Release creation failed: {error}": {
"en": "Release creation failed: {error}",
"bg": "Release creation failed: {error}",
"de": "Release creation failed: {error}",
"ru": "Release creation failed: {error}",
"zh": "Release creation failed: {error}"
},
"Release must be run on master, currently on '{branch}'.": {
"en": "Release must be run on master, currently on '{branch}'.",
"bg": "Release must be run on master, currently on '{branch}'.",
"de": "Release must be run on master, currently on '{branch}'.",
"ru": "Release must be run on master, currently on '{branch}'.",
"zh": "Release must be run on master, currently on '{branch}'."
},
"Repo must be in 'owner/name' format, got: {repo}": {
"en": "Repo must be in 'owner/name' format, got: {repo}",
"bg": "Repo must be in 'owner/name' format, got: {repo}",
"de": "Repo must be in 'owner/name' format, got: {repo}",
"ru": "Repo must be in 'owner/name' format, got: {repo}",
"zh": "Repo must be in 'owner/name' format, got: {repo}"
},
"Repository configuration complete.": {
"en": "Repository configuration complete.",
"bg": "Конфигурирането на хранилището е завършено.",
"de": "Repository-Konfiguration abgeschlossen.",
"ru": "Конфигурация репозитория завершена.",
"zh": "仓库配置完成。"
},
"Runner index {index} out of range (0..{max})": {
"en": "Runner index {index} out of range (0..{max})",
"bg": "Индексът на runner {index} е извън диапазона (0..{max})",
"de": "Runner-Index {index} außerhalb des Bereichs (0..{max})",
"ru": "Индекс runner {index} вне диапазона (0..{max})",
"zh": "Runner 索引 {index} 超出范围 (0..{max})"
},
"Running lint checks...": {
"en": "Running lint checks...",
"bg": "Running lint checks...",
"de": "Running lint checks...",
"ru": "Running lint checks...",
"zh": "Running lint checks..."
},
"Running tests...": {
"en": "Running tests...",
"bg": "Running tests...",
"de": "Running tests...",
"ru": "Running tests...",
"zh": "Running tests..."
},
"Running: {scenario} on {platform}": {
"en": "Running: {scenario} on {platform}",
"bg": "Running: {scenario} on {platform}",
"de": "Running: {scenario} on {platform}",
"ru": "Running: {scenario} on {platform}",
"zh": "Running: {scenario} on {platform}"
},
"Skipping commit push — no staged changes.": {
"en": "Skipping commit push — no staged changes.",
"bg": "Skipping commit push — no staged changes.",
"de": "Skipping commit push — no staged changes.",
"ru": "Skipping commit push — no staged changes.",
"zh": "Skipping commit push — no staged changes."
},
"Syncing {count} documentation pages to wiki...": {
"en": "Syncing {count} documentation pages to wiki...",
"bg": "Syncing {count} documentation pages to wiki...",
"de": "Syncing {count} documentation pages to wiki...",
"ru": "Syncing {count} documentation pages to wiki...",
"zh": "Syncing {count} documentation pages to wiki..."
},
"Tag consistency check failed.": {
"en": "Tag consistency check failed.",
"bg": "Tag consistency check failed.",
"de": "Tag consistency check failed.",
"ru": "Tag consistency check failed.",
"zh": "Tag consistency check failed."
},
"Tag v{version} already existed. Publish workflow should already have been triggered.": {
"en": "Tag v{version} already existed. Publish workflow should already have been triggered.",
"bg": "Tag v{version} already existed. Publish workflow should already have been triggered.",
"de": "Tag v{version} already existed. Publish workflow should already have been triggered.",
"ru": "Tag v{version} already existed. Publish workflow should already have been triggered.",
"zh": "Tag v{version} already existed. Publish workflow should already have been triggered."
},
"Tag {tag} already exists and points to HEAD. Skipping creation.": {
"en": "Tag {tag} already exists and points to HEAD. Skipping creation.",
"bg": "Tag {tag} already exists and points to HEAD. Skipping creation.",
"de": "Tag {tag} already exists and points to HEAD. Skipping creation.",
"ru": "Tag {tag} already exists and points to HEAD. Skipping creation.",
"zh": "Tag {tag} already exists and points to HEAD. Skipping creation."
},
"Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.": {
"en": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.",
"bg": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.",
"de": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.",
"ru": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.",
"zh": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details."
},
"Task ID: {task_id}": {
"en": "Task ID: {task_id}",
"bg": "Task ID: {task_id}",
"de": "Task ID: {task_id}",
"ru": "Task ID: {task_id}",
"zh": "Task ID: {task_id}"
},
"Tests failed — refusing to release. Fix test failures first.\n{stderr}": {
"en": "Tests failed — refusing to release. Fix test failures first.\n{stderr}",
"bg": "Tests failed — refusing to release. Fix test failures first.\n{stderr}",
"de": "Tests failed — refusing to release. Fix test failures first.\n{stderr}",
"ru": "Tests failed — refusing to release. Fix test failures first.\n{stderr}",
"zh": "Tests failed — refusing to release. Fix test failures first.\n{stderr}"
},
"Tests passed.": {
"en": "Tests passed.",
"bg": "Tests passed.",
"de": "Tests passed.",
"ru": "Tests passed.",
"zh": "Tests passed."
},
"Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.": {
"en": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
"bg": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
"de": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
"ru": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
"zh": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures."
},
"Unknown check category '{check}'. Available: all, user-facing{tags}": {
"en": "Unknown check category '{check}'. Available: all, user-facing{tags}",
"bg": "Unknown check category '{check}'. Available: all, user-facing{tags}",
"de": "Unknown check category '{check}'. Available: all, user-facing{tags}",
"ru": "Unknown check category '{check}'. Available: all, user-facing{tags}",
"zh": "Unknown check category '{check}'. Available: all, user-facing{tags}"
},
"Updated version in {init}": {
"en": "Updated version in {init}",
"bg": "Updated version in {init}",
"de": "Updated version in {init}",
"ru": "Updated version in {init}",
"zh": "Updated version in {init}"
},
"Updated {changelog_file}": {
"en": "Updated {changelog_file}",
"bg": "Updated {changelog_file}",
"de": "Updated {changelog_file}",
"ru": "Updated {changelog_file}",
"zh": "Updated {changelog_file}"
},
"VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.": {
"en": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.",
"bg": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.",
"de": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.",
"ru": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.",
"zh": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles."
},
"Version file: {file}": {
"en": "Version file: {file}",
"bg": "Version file: {file}",
"de": "Version file: {file}",
"ru": "Version file: {file}",
"zh": "Version file: {file}"
},
"Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.": {
"en": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.",
"bg": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.",
"de": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.",
"ru": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.",
"zh": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update."
},
"WARNING: --skip-tests passed — skipping test verification.": {
"en": "WARNING: --skip-tests passed — skipping test verification.",
"bg": "WARNING: --skip-tests passed — skipping test verification.",
"de": "WARNING: --skip-tests passed — skipping test verification.",
"ru": "WARNING: --skip-tests passed — skipping test verification.",
"zh": "WARNING: --skip-tests passed — skipping test verification."
},
"Warning: could not fetch tags from origin.": {
"en": "Warning: could not fetch tags from origin.",
"bg": "Warning: could not fetch tags from origin.",
"de": "Warning: could not fetch tags from origin.",
"ru": "Warning: could not fetch tags from origin.",
"zh": "Warning: could not fetch tags from origin."
},
"Wiki integrity check failed — {count} issue(s)": {
"en": "Wiki integrity check failed — {count} issue(s)",
"bg": "Wiki integrity check failed — {count} issue(s)",
"de": "Wiki integrity check failed — {count} issue(s)",
"ru": "Wiki integrity check failed — {count} issue(s)",
"zh": "Wiki integrity check failed — {count} issue(s)"
},
"Wiki verification failed — {failures} page(s) empty or mismatched": {
"en": "Wiki verification failed — {failures} page(s) empty or mismatched",
"bg": "Wiki verification failed — {failures} page(s) empty or mismatched",
"de": "Wiki verification failed — {failures} page(s) empty or mismatched",
"ru": "Wiki verification failed — {failures} page(s) empty or mismatched",
"zh": "Wiki verification failed — {failures} page(s) empty or mismatched"
},
"[dry-run] Would commit: release: v{version}": {
"en": "[dry-run] Would commit: release: v{version}",
"bg": "[dry-run] Would commit: release: v{version}",
"de": "[dry-run] Would commit: release: v{version}",
"ru": "[dry-run] Would commit: release: v{version}",
"zh": "[dry-run] Would commit: release: v{version}"
},
"[dry-run] Would create tag: v{version}": {
"en": "[dry-run] Would create tag: v{version}",
"bg": "[dry-run] Would create tag: v{version}",
"de": "[dry-run] Would create tag: v{version}",
"ru": "[dry-run] Would create tag: v{version}",
"zh": "[dry-run] Would create tag: v{version}"
},
"[dry-run] Would create tag: {tag}": {
"en": "[dry-run] Would create tag: {tag}",
"bg": "[dry-run] Would create tag: {tag}",
"de": "[dry-run] Would create tag: {tag}",
"ru": "[dry-run] Would create tag: {tag}",
"zh": "[dry-run] Would create tag: {tag}"
},
"[dry-run] Would push commit to master": {
"en": "[dry-run] Would push commit to master",
"bg": "[dry-run] Would push commit to master",
"de": "[dry-run] Would push commit to master",
"ru": "[dry-run] Would push commit to master",
"zh": "[dry-run] Would push commit to master"
},
"[dry-run] Would sync page: {title} ({chars} chars)": {
"en": "[dry-run] Would sync page: {title} ({chars} chars)",
"bg": "[dry-run] Would sync page: {title} ({chars} chars)",
"de": "[dry-run] Would sync page: {title} ({chars} chars)",
"ru": "[dry-run] Would sync page: {title} ({chars} chars)",
"zh": "[dry-run] Would sync page: {title} ({chars} chars)"
},
"[dry-run] Would update {changelog_file}": {
"en": "[dry-run] Would update {changelog_file}",
"bg": "[dry-run] Would update {changelog_file}",
"de": "[dry-run] Would update {changelog_file}",
"ru": "[dry-run] Would update {changelog_file}",
"zh": "[dry-run] Would update {changelog_file}"
},
"[dry-run] Would update {init}": {
"en": "[dry-run] Would update {init}",
"bg": "[dry-run] Would update {init}",
"de": "[dry-run] Would update {init}",
"ru": "[dry-run] Would update {init}",
"zh": "[dry-run] Would update {init}"
},
"active": {
"en": "active",
"bg": "активен",
"de": "aktiv",
"ru": "активен",
"zh": "活跃"
},
"completed": {
"en": "completed",
"bg": "завършен",
"de": "abgeschlossen",
"ru": "завершён",
"zh": "已完成"
},
"failed": {
"en": "failed",
"bg": "неуспешен",
"de": "fehlgeschlagen",
"ru": "неудачный",
"zh": "失败"
},
"git command failed ({cmd}): {stderr}": {
"en": "git command failed ({cmd}): {stderr}",
"bg": "git command failed ({cmd}): {stderr}",
"de": "git command failed ({cmd}): {stderr}",
"ru": "git command failed ({cmd}): {stderr}",
"zh": "git command failed ({cmd}): {stderr}"
},
"git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.": {
"en": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.",
"bg": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.",
"de": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.",
"ru": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.",
"zh": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history."
},
"git-cliff returned empty version.": {
"en": "git-cliff returned empty version.",
"bg": "git-cliff returned empty version.",
"de": "git-cliff returned empty version.",
"ru": "git-cliff returned empty version.",
"zh": "git-cliff returned empty version."
},
"git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).": {
"en": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).",
"bg": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).",
"de": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).",
"ru": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).",
"zh": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1)."
},
"in_progress": {
"en": "in progress",
"bg": "в процес",
"de": "in Bearbeitung",
"ru": "в процессе",
"zh": "进行中"
},
"inactive": {
"en": "inactive",
"bg": "неактивен",
"de": "inaktiv",
"ru": "неактивен",
"zh": "未激活"
},
"mapping.json keys and values must be strings, got {k}={v}": {
"en": "mapping.json keys and values must be strings, got {k}={v}",
"bg": "mapping.json keys and values must be strings, got {k}={v}",
"de": "mapping.json keys and values must be strings, got {k}={v}",
"ru": "mapping.json keys and values must be strings, got {k}={v}",
"zh": "mapping.json keys and values must be strings, got {k}={v}"
},
"mapping.json must be a dict of file-path -> page-title, got {type}": {
"en": "mapping.json must be a dict of file-path -> page-title, got {type}",
"bg": "mapping.json must be a dict of file-path -> page-title, got {type}",
"de": "mapping.json must be a dict of file-path -> page-title, got {type}",
"ru": "mapping.json must be a dict of file-path -> page-title, got {type}",
"zh": "mapping.json must be a dict of file-path -> page-title, got {type}"
},
"pending": {
"en": "pending",
"bg": "в очакване",
"de": "ausstehend",
"ru": "ожидает",
"zh": "待处理"
},
"unknown": {
"en": "unknown",
"bg": "неизвестен",
"de": "unbekannt",
"ru": "неизвестно",
"zh": "未知"
},
"{file} already exists. Use --force to overwrite.": {
"en": "{file} already exists. Use --force to overwrite.",
"bg": "{file} already exists. Use --force to overwrite.",
"de": "{file} already exists. Use --force to overwrite.",
"ru": "{file} already exists. Use --force to overwrite.",
"zh": "{file} already exists. Use --force to overwrite."
},
"--skip-build: skipping package build and PyPI publish.": {
"en": "--skip-build: skipping package build and PyPI publish.",
"bg": "--skip-build: skipping package build and PyPI publish.",
"de": "--skip-build: skipping package build and PyPI publish.",
"ru": "--skip-build: skipping package build and PyPI publish.",
"zh": "--skip-build: skipping package build and PyPI publish."
},
"Integration tests cancelled — another runner failed.": {
"en": "Integration tests cancelled — another runner failed.",
"bg": "Integration tests cancelled — another runner failed.",
"de": "Integration tests cancelled — another runner failed.",
"ru": "Integration tests cancelled — another runner failed.",
"zh": "Integration tests cancelled — another runner failed."
},
"Integration tests failed with exit code {code}": {
"en": "Integration tests failed with exit code {code}",
"bg": "Integration tests failed with exit code {code}",
"de": "Integration tests failed with exit code {code}",
"ru": "Integration tests failed with exit code {code}",
"zh": "Integration tests failed with exit code {code}"
},
"Integration tests passed.": {
"en": "Integration tests passed.",
"bg": "Integration tests passed.",
"de": "Integration tests passed.",
"ru": "Integration tests passed.",
"zh": "Integration tests passed."
},
"Merged {count} reports: {tests} tests, {failures} failures → {output}": {
"en": "Merged {count} reports: {tests} tests, {failures} failures → {output}",
"bg": "Merged {count} reports: {tests} tests, {failures} failures → {output}",
"de": "Merged {count} reports: {tests} tests, {failures} failures → {output}",
"ru": "Merged {count} reports: {tests} tests, {failures} failures → {output}",
"zh": "Merged {count} reports: {tests} tests, {failures} failures → {output}"
},
"No JUnit reports found matching {pattern} — skipping merge.": {
"en": "No JUnit reports found matching {pattern} — skipping merge.",
"bg": "No JUnit reports found matching {pattern} — skipping merge.",
"de": "No JUnit reports found matching {pattern} — skipping merge.",
"ru": "No JUnit reports found matching {pattern} — skipping merge.",
"zh": "No JUnit reports found matching {pattern} — skipping merge."
},
"Roles directory not found: {path}": {
"en": "Roles directory not found: {path}",
"bg": "Roles directory not found: {path}",
"de": "Roles directory not found: {path}",
"ru": "Roles directory not found: {path}",
"zh": "Roles directory not found: {path}"
},
"Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.": {
"en": "Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.",
"bg": "Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.",
"de": "Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.",
"ru": "Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.",
"zh": "Per-test speed check FAILED: {count} test(s) exceed {limit}s limit."
},
"Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls.": {
"en": "Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls.",
"bg": "Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls.",
"de": "Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls.",
"ru": "Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls.",
"zh": "Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls."
},
"Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit).": {
"en": "Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit).",
"bg": "Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit).",
"de": "Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit).",
"ru": "Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit).",
"zh": "Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit)."
}
}
+2 -1
View File
@@ -566,7 +566,8 @@ class TestVikunjaClient:
json={"done": True},
)
def test_http_error_raises_api_error(self) -> None:
@patch("devx.api_clients.time.sleep")
def test_http_error_raises_api_error(self, mock_sleep: MagicMock) -> None:
client = VikunjaClient("https://work.example.com", "tok")
mock_resp = MagicMock()
mock_resp.raise_for_status.side_effect = _mock_http_error(http.HTTPStatus.INTERNAL_SERVER_ERROR, "server error")
+164 -10
View File
@@ -1,4 +1,4 @@
"""Unit tests for scripts/check_test_speed.py."""
"""Unit tests for devx.tools.check_test_speed."""
from unittest.mock import MagicMock, patch
@@ -8,10 +8,13 @@ from click.testing import CliRunner
from devx.tools.check_test_speed import (
DEFAULT_MAX_SECONDS,
DEFAULT_MAX_SINGLE_SECONDS,
TEST_COMMAND,
check_per_test_speed,
check_speed,
cli,
parse_duration,
parse_per_test_durations,
run_tests,
)
@@ -23,12 +26,23 @@ class TestRunTests:
stdout, stderr = run_tests()
assert stdout == "out"
assert stderr == "err"
mock_run.assert_called_once_with(
TEST_COMMAND,
capture_output=True,
text=True,
check=False,
)
mock_run.assert_called_once()
call_kwargs = mock_run.call_args
assert call_kwargs.args[0] == TEST_COMMAND
assert call_kwargs.kwargs["capture_output"] is True
assert call_kwargs.kwargs["text"] is True
assert call_kwargs.kwargs["check"] is False
env = call_kwargs.kwargs["env"]
assert "--durations=0" in env["PYTEST_ADDOPTS"]
@patch("devx.tools.check_test_speed.subprocess.run")
def test_run_tests_preserves_existing_pytest_addopts(self, mock_run: MagicMock) -> None:
mock_run.return_value = MagicMock(stdout="out", stderr="err", returncode=0)
with patch.dict("os.environ", {"PYTEST_ADDOPTS": "-x"}, clear=False):
run_tests()
env = mock_run.call_args.kwargs["env"]
assert "--durations=0" in env["PYTEST_ADDOPTS"]
assert "-x" in env["PYTEST_ADDOPTS"]
class TestParseDuration:
@@ -48,6 +62,38 @@ class TestParseDuration:
assert "Could not parse" in str(exc.value)
class TestParsePerTestDurations:
def test_parses_call_lines(self) -> None:
output = "0.01s call tests/test_foo.py::test_bar\n"
durations = parse_per_test_durations(output)
assert len(durations) == 1
assert durations[0] == ("tests/test_foo.py::test_bar", 0.01)
def test_parses_setup_and_teardown(self) -> None:
output = (
"0.02s setup tests/test_foo.py::test_bar\n"
"0.01s call tests/test_foo.py::test_bar\n"
"0.00s teardown tests/test_foo.py::test_bar\n"
)
durations = parse_per_test_durations(output)
assert len(durations) == 3
names = [d[0] for d in durations]
assert "tests/test_foo.py::test_bar" in names
def test_sorted_slowest_first(self) -> None:
output = "0.01s call tests/test_a.py::test_slow\n0.50s call tests/test_b.py::test_fast\n"
durations = parse_per_test_durations(output)
assert durations[0][1] >= durations[1][1]
assert durations[0][1] == 0.50
def test_empty_output(self) -> None:
assert parse_per_test_durations("") == []
def test_ignores_non_duration_lines(self) -> None:
output = "Some random line\n234 passed in 0.70s\n"
assert parse_per_test_durations(output) == []
class TestCheckSpeed:
def test_under_budget_passes(self) -> None:
check_speed(1.0, 2.0) # should not raise
@@ -64,6 +110,31 @@ class TestCheckSpeed:
assert "max allowed: 2.0s" in msg
class TestCheckPerTestSpeed:
def test_no_violations_when_all_fast(self) -> None:
durations = [("test_a", 0.1), ("test_b", 0.2)]
assert check_per_test_speed(durations, 0.5) == []
def test_violation_when_test_exceeds_limit(self) -> None:
durations = [("test_slow", 0.6), ("test_fast", 0.1)]
violations = check_per_test_speed(durations, 0.5)
assert len(violations) == 1
assert "test_slow" in violations[0]
assert "0.60s" in violations[0]
def test_multiple_violations(self) -> None:
durations = [("test_a", 0.7), ("test_b", 0.6), ("test_c", 0.1)]
violations = check_per_test_speed(durations, 0.5)
assert len(violations) == 2
def test_exact_limit_passes(self) -> None:
durations = [("test_a", 0.5)]
assert check_per_test_speed(durations, 0.5) == []
def test_empty_durations(self) -> None:
assert check_per_test_speed([], 0.5) == []
def test_main_module_block() -> None:
import devx.tools.check_test_speed as cts
@@ -77,39 +148,71 @@ class TestMain:
@patch("devx.tools.check_test_speed.run_tests")
@patch("devx.tools.check_test_speed.parse_duration")
@patch("devx.tools.check_test_speed.check_speed")
@patch("devx.tools.check_test_speed.parse_per_test_durations")
@patch("devx.tools.check_test_speed.check_per_test_speed")
def test_successful_run(
self,
mock_check_per: MagicMock,
mock_parse_per: MagicMock,
mock_check: MagicMock,
mock_parse: MagicMock,
mock_run: MagicMock,
) -> None:
mock_run.return_value = ("stdout\n", "stderr\n")
mock_parse.return_value = 1.5
mock_parse_per.return_value = []
mock_check_per.return_value = []
runner = CliRunner()
result = runner.invoke(cli, [])
assert result.exit_code == 0
assert "1.50s" in result.output
assert "under 2.0s limit" in result.output
assert "under 10.0s limit" in result.output
mock_run.assert_called_once()
mock_parse.assert_called_once_with("stdout\n\nstderr\n")
mock_check.assert_called_once_with(1.5, DEFAULT_MAX_SECONDS)
mock_parse_per.assert_called_once()
mock_check_per.assert_called_once_with([], DEFAULT_MAX_SINGLE_SECONDS)
@patch("devx.tools.check_test_speed.run_tests")
@patch("devx.tools.check_test_speed.parse_duration")
def test_slow_tests_exit(
def test_slow_total_exits(
self,
mock_parse: MagicMock,
mock_run: MagicMock,
) -> None:
mock_run.return_value = ("out\n", "err\n")
mock_parse.return_value = 3.0
mock_parse.return_value = 15.0
runner = CliRunner()
result = runner.invoke(cli, [])
assert result.exit_code == 1
assert "too slow" in result.output.lower()
@patch("devx.tools.check_test_speed.run_tests")
@patch("devx.tools.check_test_speed.parse_duration")
@patch("devx.tools.check_test_speed.check_speed")
@patch("devx.tools.check_test_speed.parse_per_test_durations")
@patch("devx.tools.check_test_speed.check_per_test_speed")
def test_per_test_violation_exits(
self,
mock_check_per: MagicMock,
mock_parse_per: MagicMock,
mock_check: MagicMock,
mock_parse: MagicMock,
mock_run: MagicMock,
) -> None:
mock_run.return_value = ("out\n", "err\n")
mock_parse.return_value = 3.0
mock_parse_per.return_value = [("test_slow", 0.8)]
mock_check_per.return_value = ["Test 'test_slow' took 0.80s (limit: 0.5s)."]
runner = CliRunner()
result = runner.invoke(cli, [])
assert result.exit_code == 1
assert "Per-test speed check FAILED" in result.output
assert "test_slow" in result.output
@patch("devx.tools.check_test_speed.run_tests")
def test_parse_failure_exits(
self,
@@ -125,16 +228,67 @@ class TestMain:
@patch("devx.tools.check_test_speed.run_tests")
@patch("devx.tools.check_test_speed.parse_duration")
@patch("devx.tools.check_test_speed.check_speed")
@patch("devx.tools.check_test_speed.parse_per_test_durations")
@patch("devx.tools.check_test_speed.check_per_test_speed")
def test_custom_max_seconds(
self,
mock_check_per: MagicMock,
mock_parse_per: MagicMock,
mock_check: MagicMock,
mock_parse: MagicMock,
mock_run: MagicMock,
) -> None:
mock_run.return_value = ("out\n", "err\n")
mock_parse.return_value = 0.5
mock_parse_per.return_value = []
mock_check_per.return_value = []
runner = CliRunner()
result = runner.invoke(cli, ["--max-seconds", "1.5"])
assert result.exit_code == 0
mock_check.assert_called_once_with(0.5, 1.5)
@patch("devx.tools.check_test_speed.run_tests")
@patch("devx.tools.check_test_speed.parse_duration")
@patch("devx.tools.check_test_speed.check_speed")
@patch("devx.tools.check_test_speed.parse_per_test_durations")
@patch("devx.tools.check_test_speed.check_per_test_speed")
def test_disable_per_test_check(
self,
mock_check_per: MagicMock,
mock_parse_per: MagicMock,
mock_check: MagicMock,
mock_parse: MagicMock,
mock_run: MagicMock,
) -> None:
mock_run.return_value = ("out\n", "err\n")
mock_parse.return_value = 1.0
runner = CliRunner()
result = runner.invoke(cli, ["--max-single-seconds", "0"])
assert result.exit_code == 0
mock_parse_per.assert_not_called()
mock_check_per.assert_not_called()
@patch("devx.tools.check_test_speed.run_tests")
@patch("devx.tools.check_test_speed.parse_duration")
@patch("devx.tools.check_test_speed.check_speed")
@patch("devx.tools.check_test_speed.parse_per_test_durations")
@patch("devx.tools.check_test_speed.check_per_test_speed")
def test_custom_max_single_seconds(
self,
mock_check_per: MagicMock,
mock_parse_per: MagicMock,
mock_check: MagicMock,
mock_parse: MagicMock,
mock_run: MagicMock,
) -> None:
mock_run.return_value = ("out\n", "err\n")
mock_parse.return_value = 1.0
mock_parse_per.return_value = []
mock_check_per.return_value = []
runner = CliRunner()
result = runner.invoke(cli, ["--max-single-seconds", "1.0"])
assert result.exit_code == 0
mock_check_per.assert_called_once_with([], 1.0)
+3
View File
@@ -131,6 +131,7 @@ class TestCli:
clear=True,
),
patch("devx.ci.integration_guard.POLL_INTERVAL", 0.01),
patch("devx.molecule.molecule_ci_guard.POLL_INTERVAL", 0.01),
patch("devx.ci.integration_guard.subprocess.Popen") as mock_popen,
patch("devx.molecule.molecule_ci_guard.get_running_jobs", side_effect=get_jobs_side_effect),
patch("os.killpg") as mock_killpg,
@@ -177,6 +178,7 @@ class TestCli:
clear=True,
),
patch("devx.ci.integration_guard.POLL_INTERVAL", 0.01),
patch("devx.molecule.molecule_ci_guard.POLL_INTERVAL", 0.01),
patch("devx.ci.integration_guard.subprocess.Popen") as mock_popen,
patch("devx.molecule.molecule_ci_guard.get_running_jobs", side_effect=get_jobs_side_effect),
patch("os.killpg", side_effect=ProcessLookupError("no such process")),
@@ -221,6 +223,7 @@ class TestCli:
clear=True,
),
patch("devx.ci.integration_guard.POLL_INTERVAL", 0.01),
patch("devx.molecule.molecule_ci_guard.POLL_INTERVAL", 0.01),
patch("devx.ci.integration_guard.subprocess.Popen") as mock_popen,
patch("devx.molecule.molecule_ci_guard.get_running_jobs", side_effect=get_jobs_side_effect),
patch("os.killpg") as mock_killpg,