Public Access
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cd2459eef | ||
|
|
ef08513bcf | ||
|
|
05922eca2f | ||
|
|
05de2b0aa9 | ||
|
|
6a463a93d2 | ||
|
|
ad7b52c368 | ||
|
|
6b81e1a50a | ||
|
|
443dc01b4e | ||
|
|
1d7bf7118a | ||
|
|
f98534ebe2 | ||
|
|
c62b168b25 | ||
|
|
40a94df029 | ||
|
|
f50c4c1e00 | ||
|
|
bbb264efc9 | ||
|
|
c97b249935 | ||
|
|
32b9a53151 | ||
|
|
ae68df63f1 | ||
|
|
6402f31345 | ||
|
|
f017fec8f5 |
+4
-20
@@ -32,31 +32,15 @@ jobs:
|
||||
run: |
|
||||
. .venv/bin/activate 2>/dev/null || true
|
||||
python3 -m devx.tools.check_test_speed --max-seconds 6 --max-single-seconds 0.5
|
||||
- name: Documentation coverage check
|
||||
env:
|
||||
PYTHONPATH: src
|
||||
run: |
|
||||
. .venv/bin/activate 2>/dev/null || true
|
||||
python3 -m devx.ci.doc_coverage --fail-on-missing
|
||||
- name: Documentation lint check
|
||||
env:
|
||||
PYTHONPATH: src
|
||||
run: |
|
||||
. .venv/bin/activate 2>/dev/null || true
|
||||
python3 -m devx.ci.lint_docs --root .
|
||||
- name: Documentation version reference check
|
||||
env:
|
||||
PYTHONPATH: src
|
||||
run: |
|
||||
. .venv/bin/activate 2>/dev/null || true
|
||||
python3 -m devx.tools.check_doc_versions --root .
|
||||
- name: Vale prose lint check
|
||||
- name: Documentation gate (coverage + stale refs + lint + version refs + prose)
|
||||
env:
|
||||
PYTHONPATH: src
|
||||
DEVX_DOC_COVERAGE_STRICT: "1"
|
||||
DEVX_VALE_LEVEL: warning
|
||||
run: |
|
||||
. .venv/bin/activate 2>/dev/null || true
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
vale --minAlertLevel=error docs/ AGENTS.md README.md
|
||||
make devx-docs-check
|
||||
- name: Translation completeness check
|
||||
env:
|
||||
PYTHONPATH: src
|
||||
|
||||
+4
-10
@@ -73,18 +73,12 @@ repos:
|
||||
pass_filenames: false
|
||||
stages: [pre-commit]
|
||||
|
||||
- id: doc-coverage
|
||||
name: documentation coverage check
|
||||
entry: env PYTHONPATH=src .venv/bin/python -m devx.ci.doc_coverage --fail-on-missing
|
||||
language: system
|
||||
pass_filenames: false
|
||||
stages: [pre-commit]
|
||||
|
||||
- id: lint-docs
|
||||
name: documentation lint check
|
||||
entry: env PYTHONPATH=src .venv/bin/python -m devx.ci.lint_docs --root .
|
||||
- id: docs-check
|
||||
name: documentation gate (coverage + stale refs + lint + version refs + prose)
|
||||
entry: bash -c 'PYTHONPATH=src DEVX_DOC_COVERAGE_STRICT=1 DEVX_VALE_LEVEL=warning make devx-docs-check'
|
||||
language: system
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
stages: [pre-commit]
|
||||
|
||||
- id: pytest-cov
|
||||
|
||||
@@ -32,14 +32,17 @@ write-good.E-Prime = NO
|
||||
write-good.So = NO
|
||||
write-good.ThereIs = NO
|
||||
write-good.TooWordy = NO
|
||||
write-good.Passive = NO
|
||||
|
||||
# Vale defaults — spelling catches too many technical terms
|
||||
Vale.Terms = NO
|
||||
Vale.Repetition = NO
|
||||
Vale.Spelling = NO
|
||||
|
||||
# Readability — warnings only, technical docs are naturally complex
|
||||
# Readability — technical docs are naturally complex, downgrade to suggestions
|
||||
Readability.FleschReadingEase = suggestion
|
||||
Readability.FleschKincaid = suggestion
|
||||
Readability.AutomatedReadability = suggestion
|
||||
Readability.ColemanLiau = suggestion
|
||||
Readability.LIX = suggestion
|
||||
Readability.GunningFog = suggestion
|
||||
|
||||
@@ -3,4 +3,4 @@ message: "Unlabeled code block — add a language tag (```bash, ```yaml, etc.)"
|
||||
level: warning
|
||||
scope: raw
|
||||
raw:
|
||||
- '(?s)```\n(?!.*```)'
|
||||
- '(?ms)^\n```\n.*?^```\s*$'
|
||||
|
||||
@@ -2,7 +2,7 @@ Based on [write-good](https://github.com/btford/write-good).
|
||||
|
||||
> Naive linter for English prose for developers who can't write good and wanna learn to do other stuff good too.
|
||||
|
||||
```text
|
||||
```
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Brian Ford
|
||||
|
||||
@@ -226,14 +226,14 @@ After a PR is merged to master, the **post-merge workflow**
|
||||
- Pushes both the commit and tag to master
|
||||
|
||||
3. **sync-wiki** — Syncs documentation to the Gitea wiki. Runs for ALL
|
||||
non-release commits (not just when release succeeds), so docs-only
|
||||
non-release commits (not only when release succeeds), so docs-only
|
||||
changes still update the wiki.
|
||||
|
||||
4. **badges** — Generates and pushes quality badge SVGs to the `badges` branch.
|
||||
Uses `if: always()` so it runs on every push, including release commits.
|
||||
|
||||
5. **vikunja** — Marks the corresponding Vikunja task as done. Runs for ALL
|
||||
non-release commits (not just when release succeeds), so infrastructure-only
|
||||
non-release commits (not only when release succeeds), so infrastructure-only
|
||||
changes still update the task tracker.
|
||||
|
||||
6. **publish** — Runs after release succeeds (needs: release). Builds and
|
||||
@@ -398,7 +398,7 @@ system loads `.env` automatically via `python-dotenv`.
|
||||
|
||||
### pyproject.toml [tool.devx] Configuration
|
||||
|
||||
In addition to `DEVX_` env vars, several devx tools read configuration from
|
||||
In addition to `DEVX_` env vars, many devx tools read configuration from
|
||||
the `[tool.devx]` section in `pyproject.toml`. This allows per-project
|
||||
customization without environment variables.
|
||||
|
||||
@@ -610,7 +610,7 @@ the user should not need to specify which profile to use.
|
||||
2. **Background by default, foreground when blocking.**
|
||||
3. **Provide full context in the prompt** — subagents don't inherit conversation history.
|
||||
4. **One subagent per concern.** Chain: investigate → fix in main session → review.
|
||||
5. **Don't delegate trivial work** (<30s, <50 lines of context).
|
||||
5. **Don't delegate minor work** (<30s, <50 lines of context).
|
||||
6. **Compact after subagent returns.**
|
||||
7. **Never skip delegation to save time** — it keeps main context small.
|
||||
|
||||
|
||||
@@ -2,6 +2,54 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.37.0] - 2026-07-07
|
||||
|
||||
### Features
|
||||
|
||||
- Consolidate docs checks into devx-docs-check target
|
||||
|
||||
## [0.36.2] - 2026-07-07
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- GiteaClient.set_repo_variable uses PUT instead of PATCH
|
||||
|
||||
## [0.36.1] - 2026-07-07
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Preserve .badges/ dir during git clean in push_badges
|
||||
|
||||
## [0.36.0] - 2026-07-07
|
||||
|
||||
### Features
|
||||
|
||||
- Add GiteaClient repo variable methods and parallelize pytest-cov
|
||||
|
||||
## [0.35.7] - 2026-07-06
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Use Gitea wiki dash-marker filename convention
|
||||
|
||||
## [0.35.6] - 2026-07-06
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Add delay before wiki verification to avoid race condition
|
||||
|
||||
## [0.35.5] - 2026-07-06
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Embed token in wiki clone URL for push auth
|
||||
|
||||
## [0.35.4] - 2026-07-06
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Configure git identity before commit in sync_wiki
|
||||
|
||||
## [0.35.3] - 2026-07-06
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -16,12 +16,12 @@ quality badges.
|
||||
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
|
||||
## Why devx?
|
||||
|
||||
@@ -87,7 +87,7 @@ extra index and list devx in your dependencies:
|
||||
```toml
|
||||
[project]
|
||||
dependencies = [
|
||||
"devx>=0.35.3",
|
||||
"devx>=0.37.0",
|
||||
]
|
||||
|
||||
[tool.pip]
|
||||
@@ -101,8 +101,8 @@ pip install -e .
|
||||
```
|
||||
|
||||
> **Note:** If your project requires a specific devx version, pin it in
|
||||
> `dependencies` (for example, `"devx==0.35.3"`) or use a version constraint
|
||||
> (for example, `"devx>=0.35.3,<0.36"`).
|
||||
> `dependencies` (for example, `"devx==0.37.0"`) or use a version constraint
|
||||
> (for example, `"devx>=0.37.0,<0.38"`).
|
||||
|
||||
### Optional extras
|
||||
|
||||
@@ -372,7 +372,7 @@ infrastructure = []
|
||||
|
||||
# Files that would default to user-facing but are actually infrastructure
|
||||
infrastructure_overrides = [
|
||||
"src/myproject/__init__.py", # only contains __version__
|
||||
"src/myproject/__init__.py", # example only — only contains __version__
|
||||
]
|
||||
|
||||
# Safety override for broad infrastructure patterns
|
||||
|
||||
@@ -13,7 +13,7 @@ RUN pip install --no-cache-dir /tmp/devx[lint] \
|
||||
&& rm -rf /tmp/devx
|
||||
|
||||
# Install CI/CD binary tools
|
||||
RUN python3 -m devx.tools.install_tools --tool actionlint \
|
||||
RUN python3 -m devx.tools.install_tools --tool actionlint --tool vale \
|
||||
&& python3 -m devx.tools.install_checkmake
|
||||
|
||||
# Install hadolint (Dockerfile linter)
|
||||
|
||||
+8
-8
@@ -12,12 +12,12 @@ project to be reusable across all oblachno-oss repositories.
|
||||
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -74,14 +74,14 @@ Add devx to your `pyproject.toml` dependencies and configure the registry:
|
||||
```toml
|
||||
[project]
|
||||
dependencies = [
|
||||
"devx>=0.35.3",
|
||||
"devx>=0.37.0",
|
||||
]
|
||||
|
||||
[tool.pip]
|
||||
extra-index-url = "https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple"
|
||||
```
|
||||
|
||||
Pin a specific version if needed: `"devx==0.35.3"` or `"devx>=0.35.3,<0.36"`.
|
||||
Pin a specific version if needed: `"devx==0.37.0"` or `"devx>=0.37.0,<0.38"`.
|
||||
|
||||
### Optional extras
|
||||
|
||||
|
||||
@@ -122,7 +122,9 @@ exponential backoff (2s, 4s, 8s).
|
||||
- Labels (list, create, add to issues)
|
||||
- Issues (create, list)
|
||||
- Pull requests (get commits, merge, create review)
|
||||
- Releases (list)
|
||||
- Releases (list, create idempotent)
|
||||
- Actions (list runs, list jobs, get job logs)
|
||||
- Actions variables (get, set idempotent)
|
||||
- Wiki pages (list, fetch, create, update, delete)
|
||||
|
||||
**`VikunjaClient`** — Vikunja REST API wrapper:
|
||||
@@ -309,7 +311,7 @@ from `devx.api_clients`, `devx.config`, and `devx.gitea_cli`.
|
||||
### `setup.py`
|
||||
|
||||
Project setup: installs Python dependencies (editable mode with extras),
|
||||
Ansible Galaxy collections (if `ansible/requirements.yml` exists), pre-commit
|
||||
Ansible Galaxy collections (if `ansible/requirements.yml` exists in the target repo), pre-commit
|
||||
hooks (pre-commit, commit-msg, pre-push), and configures the `tea` CLI login
|
||||
profile from `.env`. Supports `--extras` to specify dependency groups,
|
||||
`--no-pre-commit` to skip hook installation, and `--no-tea-login` to skip tea
|
||||
|
||||
@@ -251,7 +251,7 @@ badges using `python -m devx.ci.push_badges`:
|
||||
|
||||
1. **Fetch latest master** — `git fetch origin master && git reset --hard
|
||||
origin/master` (ensures the version badge reflects the current state,
|
||||
even if the release job just pushed a new version)
|
||||
even if the release job recently pushed a new version)
|
||||
2. **Generate badges** — calls `devx.tools.generate_badges` which runs
|
||||
pytest-cov, doc-coverage, lint checks, and version extraction, then writes
|
||||
SVG files: `coverage.svg`, `tests.svg`, `docs.svg`, `quality.svg`,
|
||||
|
||||
@@ -405,7 +405,7 @@ devx tools install-tools --list # list status
|
||||
### `devx tools setup`
|
||||
|
||||
Project setup: install Python dependencies (editable mode with extras),
|
||||
Ansible Galaxy collections (if `ansible/requirements.yml` exists), pre-commit
|
||||
Ansible Galaxy collections (if `ansible/requirements.yml` exists in the target repo), pre-commit
|
||||
hooks (pre-commit, commit-msg, pre-push), and configure the tea CLI login
|
||||
profile from `.env`.
|
||||
|
||||
|
||||
@@ -48,12 +48,12 @@ Add devx to your `pyproject.toml`:
|
||||
```toml
|
||||
[project]
|
||||
dependencies = [
|
||||
"devx>=0.35.3",
|
||||
"devx>=0.37.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"devx>=0.35.3",
|
||||
"devx>=0.37.0",
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
@@ -121,6 +121,12 @@ vikunja_project_id = 8
|
||||
repo_owner = "oblachno-oss"
|
||||
repo_name = "devx"
|
||||
|
||||
[tool.devx.check_agent_docs]
|
||||
skip_ref_prefixes = [
|
||||
"src/myproject/",
|
||||
"ansible/requirements.yml",
|
||||
]
|
||||
|
||||
# 3. infrastructure (DEFAULT_INFRASTRUCTURE + project-specific patterns)
|
||||
# 4. Default: user-facing (safe)
|
||||
[tool.devx.classify]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""devx — reusable development and CI/CD tools for oblachno-oss projects."""
|
||||
|
||||
__version__ = "0.35.3"
|
||||
__version__ = "0.37.0"
|
||||
|
||||
@@ -372,6 +372,36 @@ class GiteaClient:
|
||||
r = self._request("GET", f"/actions/jobs/{job_id}/logs")
|
||||
return r.text
|
||||
|
||||
# -- actions variables (repo-level) --
|
||||
|
||||
def get_repo_variable(self, name: str) -> str | None:
|
||||
"""Read a Gitea Actions repository variable.
|
||||
|
||||
Returns the variable value, or ``None`` if the variable is not set.
|
||||
Raises :class:`APIError` on other HTTP errors.
|
||||
"""
|
||||
try:
|
||||
r = self._request("GET", f"/actions/variables/{name}")
|
||||
return r.json().get("value")
|
||||
except APIError as e:
|
||||
if e.status == 404:
|
||||
return None
|
||||
raise
|
||||
|
||||
def set_repo_variable(self, name: str, value: str) -> None:
|
||||
"""Create or update a Gitea Actions repository variable (idempotent).
|
||||
|
||||
Tries PUT first (update); if the variable doesn't exist (404),
|
||||
creates it via POST. Gitea 1.26.x does not support PATCH for
|
||||
action variables.
|
||||
"""
|
||||
try:
|
||||
self._request("PUT", f"/actions/variables/{name}", json={"value": value})
|
||||
except APIError as e:
|
||||
if e.status != 404:
|
||||
raise
|
||||
self._request("POST", f"/actions/variables/{name}", json={"value": value})
|
||||
|
||||
|
||||
class VikunjaClient:
|
||||
"""Low-level Vikunja REST API client with connection pooling."""
|
||||
|
||||
@@ -185,6 +185,7 @@ def main(translations: tuple[Path, ...], source_dir: str | None) -> None:
|
||||
# Try common locations
|
||||
candidates = [
|
||||
root / "src" / "devx" / "translations.json",
|
||||
root / "src" / "grm" / "translations.json",
|
||||
]
|
||||
# Also search for any translations.json in src/
|
||||
for match in root.glob("src/*/translations.json"):
|
||||
|
||||
@@ -21,6 +21,7 @@ from pathlib import Path
|
||||
|
||||
import click
|
||||
|
||||
from devx.config import _load_pyproject_devx
|
||||
from devx.i18n import _
|
||||
|
||||
# Default to the current working directory (consuming repo's root)
|
||||
@@ -71,12 +72,30 @@ def extract_cli_commands(source_dir: Path) -> list[str]:
|
||||
# Matches @cli.command, @ci.command, @tools.command, @molecule.command
|
||||
for match in re.finditer(r"@\w+\.command\b", content):
|
||||
# Check for explicit name="..." in the decorator arguments
|
||||
decorator_end = content.find(")", match.start())
|
||||
# Use a balanced paren search to find the end of the decorator
|
||||
# (handles nested parens like @cli.command(help=_("...")))
|
||||
depth = 0
|
||||
decorator_end = match.start()
|
||||
for i in range(match.start(), len(content)):
|
||||
if content[i] == "(":
|
||||
depth += 1
|
||||
elif content[i] == ")":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
decorator_end = i
|
||||
break
|
||||
decorator_text = content[match.start() : decorator_end + 1]
|
||||
name_match = re.search(r'["\']([^"\']+)["\']', decorator_text)
|
||||
# Look for explicit name="..." parameter (not help=, not other kwargs)
|
||||
name_match = re.search(r'\bname\s*=\s*["\']([^"\']+)["\']', decorator_text)
|
||||
if name_match:
|
||||
commands.append(name_match.group(1))
|
||||
continue
|
||||
# Look for a positional string argument (e.g. @cli.command("my-cmd"))
|
||||
# but skip if the only strings are in help= or other keyword args
|
||||
positional_match = re.search(r'@\w+\.command\s*\(\s*["\']([^"\']+)["\']', decorator_text)
|
||||
if positional_match:
|
||||
commands.append(positional_match.group(1))
|
||||
continue
|
||||
# Find the next def statement after this decorator
|
||||
after = content[decorator_end:]
|
||||
def_match = re.search(r"def\s+(\w+)\s*\(", after)
|
||||
@@ -106,16 +125,38 @@ def check_module_documented(module: str, docs_content: str) -> bool:
|
||||
@click.command()
|
||||
@click.option("--docs-dir", default=None, help="Path to the docs directory (default: ./docs).")
|
||||
@click.option("--source-dir", default=None, help="Path to the source directory (default: auto-detect from src/).")
|
||||
@click.option(
|
||||
"--ci-scripts-dir",
|
||||
default=None,
|
||||
help=(
|
||||
"Path to CI scripts directory (default: auto-detect from src/ci/). "
|
||||
"Set to empty string to skip CI script checks."
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
"--fail-on-missing",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Exit with non-zero status if any documentation is missing.",
|
||||
)
|
||||
def main(docs_dir: str | None, source_dir: str | None, fail_on_missing: bool) -> None:
|
||||
def main(docs_dir: str | None, source_dir: str | None, ci_scripts_dir: str | None, fail_on_missing: bool) -> None:
|
||||
root = Path.cwd()
|
||||
docs_path = Path(docs_dir) if docs_dir else root / "docs"
|
||||
|
||||
# Read [tool.devx.doc_coverage] config from pyproject.toml
|
||||
devx_cfg = _load_pyproject_devx()
|
||||
doc_cov_cfg_raw: object = devx_cfg.get("doc_coverage", {}) if isinstance(devx_cfg, dict) else {}
|
||||
doc_cov_cfg: dict[str, object] = doc_cov_cfg_raw if isinstance(doc_cov_cfg_raw, dict) else {}
|
||||
|
||||
# CLI args override config; config overrides defaults
|
||||
if ci_scripts_dir is None and "ci_scripts_dir" in doc_cov_cfg:
|
||||
ci_scripts_dir = str(doc_cov_cfg["ci_scripts_dir"])
|
||||
if docs_dir is None and "docs_dir" in doc_cov_cfg:
|
||||
docs_dir = str(doc_cov_cfg["docs_dir"])
|
||||
docs_path = Path(docs_dir)
|
||||
if source_dir is None and "source_dir" in doc_cov_cfg:
|
||||
source_dir = str(doc_cov_cfg["source_dir"])
|
||||
|
||||
# Auto-detect source directory
|
||||
if source_dir:
|
||||
src_path = Path(source_dir)
|
||||
@@ -166,13 +207,27 @@ def main(docs_dir: str | None, source_dir: str | None, fail_on_missing: bool) ->
|
||||
missing.append(f"Module: {module}")
|
||||
|
||||
# Check CI scripts in ci-cd-workflow.md
|
||||
# Auto-detect CI scripts from ci/ subdirectory
|
||||
# Auto-detect CI scripts from ci/ subdirectory, or use explicit config
|
||||
click.echo(_("\nChecking CI script documentation in ci-cd-workflow.md..."))
|
||||
ci_dir = src_path / "ci" if src_path.name != "ci" else src_path
|
||||
if ci_dir.exists():
|
||||
detected_scripts = sorted(f.name for f in ci_dir.glob("*.py") if f.name != "__init__.py")
|
||||
if ci_scripts_dir is not None:
|
||||
# Explicit config — empty string means skip CI script checks
|
||||
if ci_scripts_dir == "":
|
||||
detected_scripts = []
|
||||
else:
|
||||
ci_dir = Path(ci_scripts_dir)
|
||||
if ci_dir.exists():
|
||||
detected_scripts = sorted(f.name for f in ci_dir.glob("*.py") if f.name != "__init__.py")
|
||||
else:
|
||||
detected_scripts = []
|
||||
else:
|
||||
detected_scripts = REQUIRED_SCRIPTS
|
||||
# Auto-detect from src_path/ci/
|
||||
ci_dir = src_path / "ci" if src_path.name != "ci" else src_path
|
||||
if ci_dir.exists():
|
||||
detected_scripts = sorted(f.name for f in ci_dir.glob("*.py") if f.name != "__init__.py")
|
||||
else:
|
||||
# No ci/ directory found — skip CI script checks rather than falling back
|
||||
# to REQUIRED_SCRIPTS (which is devx-specific)
|
||||
detected_scripts = []
|
||||
total += len(detected_scripts)
|
||||
ci_docs = ci_cd_file.read_text() if ci_cd_file.exists() else ""
|
||||
for script in detected_scripts:
|
||||
|
||||
@@ -93,8 +93,8 @@ def push_to_badges_branch(badges_dir: str) -> str:
|
||||
_run(["git", "config", "user.email", "actions@oblachno.fyi"]) # nosec B607
|
||||
_run(["git", "checkout", "--orphan", "badges"]) # nosec B607
|
||||
_run(["git", "rm", "-rf", "."]) # nosec B607
|
||||
# Remove untracked files/dirs left behind (e.g. .badges/ from generate_badges)
|
||||
_run(["git", "clean", "-fdx", "-e", ".git"]) # nosec B607
|
||||
# Remove untracked files/dirs left behind, but preserve .badges/ for copy below
|
||||
_run(["git", "clean", "-fdx", "-e", ".git", "-e", badges_dir]) # nosec B607
|
||||
|
||||
# Copy badge files to root
|
||||
for svg in Path(badges_dir).glob("*.svg"):
|
||||
|
||||
+47
-11
@@ -31,7 +31,9 @@ import os
|
||||
import re
|
||||
import subprocess # nosec B404
|
||||
import tempfile
|
||||
import time
|
||||
from pathlib import Path
|
||||
from urllib.parse import quote, urlparse
|
||||
|
||||
import click
|
||||
from dotenv import load_dotenv # pyright: ignore[reportMissingImports,reportUnknownVariableType]
|
||||
@@ -48,6 +50,23 @@ MAPPING_FILE = DOCS_DIR / "mapping.json"
|
||||
_LINK_RE = re.compile(r"\[([^\]]*)\]\(([^)]+)\)")
|
||||
|
||||
|
||||
def wiki_filename(page_title: str) -> str:
|
||||
"""Convert a wiki page title to its Gitea wiki filename.
|
||||
|
||||
Gitea uses a "dash marker" (``.-``) suffix to distinguish literal dashes
|
||||
from space-to-dash conversions. See Gitea's ``services/wiki/wiki_path.go``.
|
||||
|
||||
- "Architecture" (no dashes) → ``Architecture.md``
|
||||
- "Getting-Started" (has dashes) → ``Getting-Started.-.md``
|
||||
- "Home" (no dashes) → ``Home.md``
|
||||
"""
|
||||
name = page_title.replace(" ", "-")
|
||||
if "-" in name:
|
||||
name += ".-"
|
||||
name += ".md"
|
||||
return quote(name, safe="")
|
||||
|
||||
|
||||
def load_mapping() -> dict[str, str]:
|
||||
"""Load the file-to-wiki-page mapping from mapping.json."""
|
||||
with open(MAPPING_FILE, encoding="utf-8") as f:
|
||||
@@ -102,7 +121,10 @@ def get_wiki_clone_url(owner: str, repo: str, token: str) -> str:
|
||||
# Gitea wiki repos are at {clone_url}.wiki.git
|
||||
# Extract base URL from API URL
|
||||
base = GITEA_API_URL.rsplit("/api/v1", 1)[0]
|
||||
return f"{base}/{owner}/{repo}.wiki.git"
|
||||
# Embed token in URL for both clone and push auth
|
||||
# Format: https://token@host/owner/repo.wiki.git
|
||||
parsed = urlparse(base)
|
||||
return f"{parsed.scheme}://{token}@{parsed.hostname}/{owner}/{repo}.wiki.git"
|
||||
|
||||
|
||||
def clone_wiki(wiki_url: str, dest: Path) -> bool:
|
||||
@@ -166,16 +188,15 @@ def sync_files(
|
||||
# Transform links
|
||||
transformed = transform_links(content)
|
||||
|
||||
# Wiki filename: use the page title with spaces → underscores
|
||||
# Gitea wiki uses the page title as filename (spaces become dashes)
|
||||
wiki_filename = page_title.replace(" ", "-") + ".md"
|
||||
expected_files.add(wiki_filename)
|
||||
# Wiki filename: Gitea uses a dash-marker convention for titles with dashes
|
||||
fname = wiki_filename(page_title)
|
||||
expected_files.add(fname)
|
||||
|
||||
if not dry_run:
|
||||
dest = wiki_dir / wiki_filename
|
||||
dest = wiki_dir / fname
|
||||
dest.write_text(transformed, encoding="utf-8")
|
||||
synced += 1
|
||||
click.echo(_(" Synced: {title} → {file}", title=page_title, file=wiki_filename))
|
||||
click.echo(_(" Synced: {title} → {file}", title=page_title, file=fname))
|
||||
|
||||
# Prune stale pages (in wiki but not in mapping)
|
||||
pruned = 0
|
||||
@@ -208,7 +229,19 @@ def commit_and_push(wiki_dir: Path, wiki_url: str, dry_run: bool) -> bool:
|
||||
click.echo(_("No changes to sync — wiki is up to date."))
|
||||
return False
|
||||
|
||||
# Commit
|
||||
# Commit — ensure git identity is configured (CI environments may lack it)
|
||||
subprocess.run( # nosec
|
||||
["git", "config", "user.email", "devin-ai-integration[bot]@users.noreply.github.com"],
|
||||
cwd=wiki_dir,
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
subprocess.run( # nosec
|
||||
["git", "config", "user.name", "Devin CI"],
|
||||
cwd=wiki_dir,
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
subprocess.run( # nosec
|
||||
["git", "commit", "-m", "Sync wiki from docs/ [skip ci]"],
|
||||
cwd=wiki_dir,
|
||||
@@ -218,7 +251,7 @@ def commit_and_push(wiki_dir: Path, wiki_url: str, dry_run: bool) -> bool:
|
||||
|
||||
# Push
|
||||
result = subprocess.run( # nosec
|
||||
["git", "push", wiki_url, "HEAD:master"],
|
||||
["git", "push", "--force", wiki_url, "HEAD:master"],
|
||||
cwd=wiki_dir,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
@@ -293,6 +326,9 @@ def main(dry_run: bool, repo: str | None, verify: bool) -> None:
|
||||
|
||||
# Verification
|
||||
if verify and not dry_run:
|
||||
if pushed:
|
||||
click.echo(_("Waiting 5s for Gitea to process pushed commits..."))
|
||||
time.sleep(5)
|
||||
click.echo(_("\nVerifying wiki pages..."))
|
||||
# Re-clone to verify
|
||||
verify_dir = Path(tmpdir) / "verify"
|
||||
@@ -301,8 +337,8 @@ def main(dry_run: bool, repo: str | None, verify: bool) -> None:
|
||||
raise click.ClickException(_("Wiki verification failed — could not clone wiki"))
|
||||
failures = 0
|
||||
for _file_path, page_title in sorted(mapping.items()):
|
||||
wiki_filename = page_title.replace(" ", "-") + ".md"
|
||||
if (verify_dir / wiki_filename).exists():
|
||||
fname = wiki_filename(page_title)
|
||||
if (verify_dir / fname).exists():
|
||||
click.echo(_(" OK: {title}", title=page_title))
|
||||
else:
|
||||
click.echo(_(" FAIL: {title} — page not found in wiki!", title=page_title))
|
||||
|
||||
+43
-3
@@ -39,6 +39,9 @@
|
||||
# DEVX_GITEA_PYPI_ORG — Gitea PyPI org (default: oblachno-oss)
|
||||
# DEVX_ACTIONLINT_CFG — actionlint config file (default: .gitea/actionlint.yaml)
|
||||
# DEVX_WORKFLOW_DIR — workflow directory (default: .gitea/workflows)
|
||||
# DEVX_DOC_COVERAGE_STRICT — fail on missing docs (default: 0)
|
||||
# DEVX_DOC_VERSIONS_PKG — package name for version ref checks (default: auto)
|
||||
# DEVX_VALE_LEVEL — vale alert threshold (default: warning)
|
||||
|
||||
DEVX_PYTHON ?= python3
|
||||
DEVX_PR_BASE ?= master
|
||||
@@ -52,6 +55,7 @@ DEVX_GITEA_PYPI_ORG ?= oblachno-oss
|
||||
DEVX_ACTIONLINT_CFG ?= .gitea/actionlint.yaml
|
||||
DEVX_WORKFLOW_DIR ?= .gitea/workflows
|
||||
DEVX_DOCKERFILE_PATHS ?= docker
|
||||
DEVX_VALE_LEVEL ?= warning
|
||||
|
||||
# PIP_INSTALL — helper to run pip with Gitea private PyPI registry configured.
|
||||
# Usage: $(DEVX_PIP_INSTALL) install -e '.[ci,lint]'
|
||||
@@ -304,7 +308,7 @@ devx-test-unit:
|
||||
@$(DEVX_BIN)/pytest $(DEVX_TEST_PATHS) -q --no-cov
|
||||
|
||||
devx-pytest-cov:
|
||||
@$(DEVX_BIN)/pytest $(DEVX_TEST_PATHS) -v --cov=$(DEVX_COV_PKG) --cov-report=term-missing --cov-fail-under=100
|
||||
@$(DEVX_BIN)/pytest $(DEVX_TEST_PATHS) -n auto --cov=$(DEVX_COV_PKG) --cov-report=term-missing --cov-fail-under=100
|
||||
|
||||
# ── Quality checks ────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -328,10 +332,46 @@ devx-check-docs:
|
||||
devx-check-doc-versions:
|
||||
@$(DEVX_PYTHON) -m devx.tools.check_doc_versions --root .
|
||||
|
||||
# Run Vale prose linter on docs and README
|
||||
# Documentation coverage — checks that all modules/scripts/CLI commands
|
||||
# are documented. Fails if any are missing when DEVX_DOC_COVERAGE_STRICT=1.
|
||||
devx-doc-coverage:
|
||||
@$(DEVX_PYTHON) -m devx.ci.doc_coverage $(if $(filter 1,$(DEVX_DOC_COVERAGE_STRICT)),--fail-on-missing)
|
||||
|
||||
# All-in-one documentation gate: coverage + stale refs + structural lint +
|
||||
# version refs + prose lint. Use in CI and pre-commit as a single step
|
||||
# instead of 5+ separate steps.
|
||||
#
|
||||
# Configuration via environment variables (set in Makefile before include
|
||||
# or in CI env):
|
||||
# DEVX_DOC_COVERAGE_STRICT=1 — fail on missing docs (recommended)
|
||||
# DEVX_DOC_VERSIONS_PKG=<pkg> — enable version ref checks for a named package
|
||||
# DEVX_VALE_LEVEL=<level> — vale alert threshold (error, warning, suggestion)
|
||||
# default: warning (catches weasel words, unlabeled
|
||||
# code blocks, etc. — not just spelling errors)
|
||||
devx-docs-check: devx-doc-coverage devx-check-docs
|
||||
@$(DEVX_PYTHON) -m devx.ci.lint_docs --root .
|
||||
@if [ -n "$(DEVX_DOC_VERSIONS_PKG)" ]; then \
|
||||
$(DEVX_PYTHON) -m devx.tools.check_doc_versions --root . --package $(DEVX_DOC_VERSIONS_PKG); \
|
||||
elif $(DEVX_PYTHON) -c "import importlib.util,sys; sys.exit(0 if any(importlib.util.find_spec(p) for p in ['devx','grm','oblachno_infra']) else 1)" 2>/dev/null; then \
|
||||
$(DEVX_PYTHON) -m devx.tools.check_doc_versions --root . 2>/dev/null || true; \
|
||||
fi
|
||||
@export PATH="$$HOME/.local/bin:$$PATH" && \
|
||||
if ! command -v vale >/dev/null 2>&1; then \
|
||||
echo "[devx-docs-check] vale not installed — skipping prose lint (install with 'make install-tools')"; \
|
||||
else \
|
||||
vale sync >/dev/null 2>&1 || true; \
|
||||
vale --minAlertLevel=$(DEVX_VALE_LEVEL) docs/ AGENTS.md README.md; \
|
||||
fi
|
||||
|
||||
# Run Vale prose linter on docs and README (skips if vale not installed)
|
||||
# Legacy target — use devx-docs-check for the full documentation gate.
|
||||
devx-vale:
|
||||
@export PATH="$$HOME/.local/bin:$$PATH" && \
|
||||
vale --minAlertLevel=error docs/ AGENTS.md README.md
|
||||
if ! command -v vale >/dev/null 2>&1; then \
|
||||
echo "[devx-vale] vale not installed — skipping (install with 'make install-tools')"; \
|
||||
else \
|
||||
vale --minAlertLevel=error docs/ AGENTS.md README.md; \
|
||||
fi
|
||||
|
||||
# Verify test suite timing
|
||||
devx-check-test-speed:
|
||||
|
||||
@@ -68,8 +68,8 @@ def detect_package_name(repo_root: Path) -> str | None:
|
||||
Looks for the first subdirectory under ``src/`` that contains
|
||||
an ``__init__.py`` file with ``__version__``.
|
||||
|
||||
Returns the package directory name (e.g., ``devx``) or ``None`` if
|
||||
no package is found.
|
||||
Returns the package directory name (e.g., ``devx``,
|
||||
``grm``) or ``None`` if no package is found.
|
||||
"""
|
||||
src_dir = repo_root / "src"
|
||||
if not src_dir.is_dir():
|
||||
|
||||
+816
-616
@@ -47,13 +47,13 @@
|
||||
"ru": "\nDoc coverage: {covered}/{total} ({pct}%)",
|
||||
"zh": "\nDoc coverage: {covered}/{total} ({pct}%)"
|
||||
},
|
||||
"\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}": {
|
||||
"bg": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
|
||||
"de": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
|
||||
"en": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
|
||||
"pl": "\nGotowe! Utworzono: {created}, Zaktualizowano: {updated}, Pominięto: {skipped}",
|
||||
"ru": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
|
||||
"zh": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}"
|
||||
"\nDone! Synced: {synced}, Pruned: {pruned}": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "\nDone! Synced: {synced}, Pruned: {pruned}",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"\nDone. Deleted {deleted}, kept {kept}, failed {failed}.": {
|
||||
"bg": "\nDone. Deleted {deleted}, kept {kept}, failed {failed}.",
|
||||
@@ -71,6 +71,14 @@
|
||||
"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."
|
||||
},
|
||||
"\nFAIL: {n} stale version reference(s) found:": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "\nFAIL: {n} stale version reference(s) found:",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"\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.",
|
||||
@@ -79,6 +87,14 @@
|
||||
"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."
|
||||
},
|
||||
"\nFixed {n} stale version reference(s).": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "\nFixed {n} stale version reference(s).",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"\nGenerated {count} badges:": {
|
||||
"bg": "\nGenerated {count} badges:",
|
||||
"de": "\nGenerated {count} badges:",
|
||||
@@ -87,22 +103,6 @@
|
||||
"ru": "\nGenerated {count} badges:",
|
||||
"zh": "\nGenerated {count} badges:"
|
||||
},
|
||||
"\nIntegrity check FAILED ({count} issues):": {
|
||||
"bg": "\nIntegrity check FAILED ({count} issues):",
|
||||
"de": "\nIntegrity check FAILED ({count} issues):",
|
||||
"en": "\nIntegrity check FAILED ({count} issues):",
|
||||
"pl": "\nKontrola integralności NIEUDANA ({count} problemów):",
|
||||
"ru": "\nIntegrity check FAILED ({count} issues):",
|
||||
"zh": "\nIntegrity check FAILED ({count} issues):"
|
||||
},
|
||||
"\nIntegrity check passed — all {count} pages verified.": {
|
||||
"bg": "\nIntegrity check passed — all {count} pages verified.",
|
||||
"de": "\nIntegrity check passed — all {count} pages verified.",
|
||||
"en": "\nIntegrity check passed — all {count} pages verified.",
|
||||
"pl": "\nKontrola integralności zakończona pomyślnie — wszystkie {count} stron zweryfikowane.",
|
||||
"ru": "\nIntegrity check passed — all {count} pages verified.",
|
||||
"zh": "\nIntegrity check passed — all {count} pages verified."
|
||||
},
|
||||
"\nKeeping {kept}, would delete {count}": {
|
||||
"bg": "\nKeeping {kept}, would delete {count}",
|
||||
"de": "\nKeeping {kept}, would delete {count}",
|
||||
@@ -127,6 +127,22 @@
|
||||
"ru": "\nMissing documentation:",
|
||||
"zh": "\nMissing documentation:"
|
||||
},
|
||||
"\nNo stale version references found.": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "\nNo stale version references found.",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"\nPASS: All version references are current.": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "\nPASS: All version references are current.",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"\nResult: {status}": {
|
||||
"bg": "\nResult: {status}",
|
||||
"de": "\nResult: {status}",
|
||||
@@ -151,13 +167,13 @@
|
||||
"ru": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}'.",
|
||||
"zh": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}'."
|
||||
},
|
||||
"\nRunning full wiki integrity check...": {
|
||||
"bg": "\nRunning full wiki integrity check...",
|
||||
"de": "\nRunning full wiki integrity check...",
|
||||
"en": "\nRunning full wiki integrity check...",
|
||||
"pl": "\nUruchamianie pełnej kontroli integralności wiki...",
|
||||
"ru": "\nRunning full wiki integrity check...",
|
||||
"zh": "\nRunning full wiki integrity check..."
|
||||
"\nRun with --fix to auto-update version references.": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "\nRun with --fix to auto-update version references.",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"\nTag → Commit alignment:": {
|
||||
"bg": "\nTag → Commit alignment:",
|
||||
@@ -183,29 +199,21 @@
|
||||
"ru": "\nUser-facing changes ({count}):",
|
||||
"zh": "\nUser-facing changes ({count}):"
|
||||
},
|
||||
"\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!",
|
||||
"en": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
|
||||
"pl": "\nWeryfikacja NIEUDANA: {failures} strona(y) ma pustą lub niezgodną treść!",
|
||||
"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 exist.": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "\nVerification passed — all wiki pages exist.",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"\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.",
|
||||
"en": "\nVerification passed — all wiki pages have correct content.",
|
||||
"pl": "\nWeryfikacja zakończona pomyślnie — wszystkie strony wiki mają poprawną treść.",
|
||||
"ru": "\nVerification passed — all wiki pages have correct content.",
|
||||
"zh": "\nVerification passed — all wiki pages have correct content."
|
||||
},
|
||||
"\nVerifying wiki pages have content...": {
|
||||
"bg": "\nVerifying wiki pages have content...",
|
||||
"de": "\nVerifying wiki pages have content...",
|
||||
"en": "\nVerifying wiki pages have content...",
|
||||
"pl": "\nWeryfikowanie, czy strony wiki mają treść...",
|
||||
"ru": "\nVerifying wiki pages have content...",
|
||||
"zh": "\nVerifying wiki pages have content..."
|
||||
"\nVerifying wiki pages...": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "\nVerifying wiki pages...",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"\nWorkflow-only changes ({count}):": {
|
||||
"bg": "\nWorkflow-only changes ({count}):",
|
||||
@@ -351,6 +359,30 @@
|
||||
"ru": " - Требуемые проверки статуса: {checks}",
|
||||
"zh": " - 必需状态检查: {checks}"
|
||||
},
|
||||
" - {count} standard labels verified": {
|
||||
"bg": " - {count} standard labels verified",
|
||||
"de": " - {count} standard labels verified",
|
||||
"en": " - {count} standard labels verified",
|
||||
"pl": " - {count} standard labels verified",
|
||||
"ru": " - {count} standard labels verified",
|
||||
"zh": " - {count} standard labels verified"
|
||||
},
|
||||
" -> {dir}": {
|
||||
"en": " -> {dir}",
|
||||
"bg": " -> {dir}",
|
||||
"de": " -> {dir}",
|
||||
"pl": " -> {dir}",
|
||||
"ru": " -> {dir}",
|
||||
"zh": " -> {dir}"
|
||||
},
|
||||
" ... and {n} more": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": " ... and {n} more",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
" Auto-fixed trailing whitespace in {n} files": {
|
||||
"bg": " Auto-fixed trailing whitespace in {n} files",
|
||||
"de": " Auto-fixed trailing whitespace in {n} files",
|
||||
@@ -391,14 +423,6 @@
|
||||
"ru": " Collecting version...",
|
||||
"zh": " Collecting version..."
|
||||
},
|
||||
" Created: {title}": {
|
||||
"bg": " Created: {title}",
|
||||
"de": " Created: {title}",
|
||||
"en": " Created: {title}",
|
||||
"pl": " Utworzono: {title}",
|
||||
"ru": " Created: {title}",
|
||||
"zh": " Created: {title}"
|
||||
},
|
||||
" Deleted: {version}": {
|
||||
"bg": " Deleted: {version}",
|
||||
"de": " Deleted: {version}",
|
||||
@@ -407,13 +431,13 @@
|
||||
"ru": " Deleted: {version}",
|
||||
"zh": " Deleted: {version}"
|
||||
},
|
||||
" FAIL: {title} — content mismatch or empty!": {
|
||||
"bg": " FAIL: {title} — content mismatch or empty!",
|
||||
"de": " FAIL: {title} — content mismatch or empty!",
|
||||
"en": " FAIL: {title} — content mismatch or empty!",
|
||||
"pl": " BŁĄD: {title} — treść niezgodna lub pusta!",
|
||||
"ru": " FAIL: {title} — content mismatch or empty!",
|
||||
"zh": " FAIL: {title} — content mismatch or empty!"
|
||||
" FAIL: {title} — page not found in wiki!": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": " FAIL: {title} — page not found in wiki!",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
" FAILED to delete: {version}": {
|
||||
"bg": " FAILED to delete: {version}",
|
||||
@@ -423,6 +447,14 @@
|
||||
"ru": " FAILED to delete: {version}",
|
||||
"zh": " FAILED to delete: {version}"
|
||||
},
|
||||
" Fixed {fixes} version ref(s) in {file}": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": " Fixed {fixes} version ref(s) in {file}",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
" Generated: {path}": {
|
||||
"bg": " Generated: {path}",
|
||||
"de": " Generated: {path}",
|
||||
@@ -479,13 +511,13 @@
|
||||
"ru": " OK: {script}",
|
||||
"zh": " OK: {script}"
|
||||
},
|
||||
" OK: {title} ({chars} chars)": {
|
||||
"bg": " OK: {title} ({chars} chars)",
|
||||
"de": " OK: {title} ({chars} chars)",
|
||||
"en": " OK: {title} ({chars} chars)",
|
||||
"pl": " OK: {title} ({chars} znaków)",
|
||||
"ru": " OK: {title} ({chars} chars)",
|
||||
"zh": " OK: {title} ({chars} chars)"
|
||||
" OK: {title}": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": " OK: {title}",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
" Package: {pkg}": {
|
||||
"bg": " Package: {pkg}",
|
||||
@@ -495,6 +527,14 @@
|
||||
"ru": " Package: {pkg}",
|
||||
"zh": " Package: {pkg}"
|
||||
},
|
||||
" Pruned: {file} (not in mapping)": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": " Pruned: {file} (not in mapping)",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
" Quality checks: {checks}": {
|
||||
"bg": " Quality checks: {checks}",
|
||||
"de": " Quality checks: {checks}",
|
||||
@@ -511,6 +551,22 @@
|
||||
"ru": " Repo root: {root}",
|
||||
"zh": " Repo root: {root}"
|
||||
},
|
||||
" Run 'make install-checkmake' to install the Makefile linter.": {
|
||||
"en": " Run 'make install-checkmake' to install the Makefile linter.",
|
||||
"bg": " Изпълнете 'make install-checkmake' за инсталиране на Makefile линтера.",
|
||||
"de": " Führen Sie 'make install-checkmake' aus, um den Makefile-Linter zu installieren.",
|
||||
"pl": " Uruchom 'make install-checkmake', aby zainstalować linter Makefile.",
|
||||
"ru": " Выполните 'make install-checkmake' для установки линтера Makefile.",
|
||||
"zh": " 运行 'make install-checkmake' 来安装 Makefile 检查器。"
|
||||
},
|
||||
" Synced: {title} → {file}": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": " Synced: {title} → {file}",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
" Test paths: {testpaths}": {
|
||||
"bg": " Test paths: {testpaths}",
|
||||
"de": " Test paths: {testpaths}",
|
||||
@@ -519,13 +575,21 @@
|
||||
"ru": " Test paths: {testpaths}",
|
||||
"zh": " Test paths: {testpaths}"
|
||||
},
|
||||
" Updated: {title}": {
|
||||
"bg": " Updated: {title}",
|
||||
"de": " Updated: {title}",
|
||||
"en": " Updated: {title}",
|
||||
"pl": " Zaktualizowano: {title}",
|
||||
"ru": " Updated: {title}",
|
||||
"zh": " Updated: {title}"
|
||||
" WARN: Mapped file {file} is empty, skipping": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": " WARN: Mapped file {file} is empty, skipping",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
" WARN: Mapped file {file} not found, skipping": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": " WARN: Mapped file {file} not found, skipping",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
" WARNING: Could not extract coverage from pytest output (rc={rc})": {
|
||||
"bg": " WARNING: Could not extract coverage from pytest output (rc={rc})",
|
||||
@@ -615,6 +679,22 @@
|
||||
"ru": " {name}: {label}={message} ({color})",
|
||||
"zh": " {name}: {label}={message} ({color})"
|
||||
},
|
||||
" {n} long lines found (warnings only)": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": " {n} long lines found (warnings only)",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
" {n} orphan docs found (warnings only)": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": " {n} orphan docs found (warnings only)",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
" {n} stale docs found (warnings only)": {
|
||||
"bg": " {n} stale docs found (warnings only)",
|
||||
"de": " {n} stale docs found (warnings only)",
|
||||
@@ -623,6 +703,14 @@
|
||||
"ru": " {n} stale docs found (warnings only)",
|
||||
"zh": " {n} stale docs found (warnings only)"
|
||||
},
|
||||
" {tool}: found at {path}": {
|
||||
"en": " {tool}: found at {path}",
|
||||
"bg": " {tool}: намерен на {path}",
|
||||
"de": " {tool}: gefunden unter {path}",
|
||||
"pl": " {tool}: znaleziono w {path}",
|
||||
"ru": " {tool}: найден в {path}",
|
||||
"zh": " {tool}: 在 {path} 找到"
|
||||
},
|
||||
" {version} (created: {created})": {
|
||||
"bg": " {version} (created: {created})",
|
||||
"de": " {version} (created: {created})",
|
||||
@@ -727,6 +815,22 @@
|
||||
"ru": "Assigned {count} items to runner {runner_index}: {encoded}",
|
||||
"zh": "Assigned {count} items to runner {runner_index}: {encoded}"
|
||||
},
|
||||
"Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.": {
|
||||
"bg": "Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
|
||||
"de": "Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
|
||||
"en": "Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
|
||||
"pl": "Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
|
||||
"ru": "Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
|
||||
"zh": "Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label."
|
||||
},
|
||||
"Automated CI commit (badge) — skipping post-merge jobs.": {
|
||||
"bg": "Automated CI commit (badge) — skipping post-merge jobs.",
|
||||
"de": "Automated CI commit (badge) — skipping post-merge jobs.",
|
||||
"en": "Automated CI commit (badge) — skipping post-merge jobs.",
|
||||
"pl": "Automated CI commit (badge) — skipping post-merge jobs.",
|
||||
"ru": "Automated CI commit (badge) — skipping post-merge jobs.",
|
||||
"zh": "Automated CI commit (badge) — skipping post-merge jobs."
|
||||
},
|
||||
"Badge push attempt {attempt}/{retries} failed — retrying: {error}": {
|
||||
"bg": "Badge push attempt {attempt}/{retries} failed — retrying: {error}",
|
||||
"de": "Badge push attempt {attempt}/{retries} failed — retrying: {error}",
|
||||
@@ -775,6 +879,22 @@
|
||||
"ru": "Ветка '{branch}' не содержит ID задачи.\n Ожидаемый формат: {prefix}-N-краткое-описание\n Пример: {prefix}-42-add-feature\n Исправление: переименуйте ветку или создайте задачу Vikunja:\n python -m devx.tools.create_task --title \"Заголовок задачи\"",
|
||||
"zh": "分支 '{branch}' 不包含任务 ID。\n 预期格式: {prefix}-N-简短描述\n 示例: {prefix}-42-add-feature\n 修复: 重命名分支或先创建 Vikunja 任务:\n python -m devx.tools.create_task --title \"任务标题\""
|
||||
},
|
||||
"Branch is already up-to-date with origin/master.": {
|
||||
"bg": "Branch is already up-to-date with origin/master.",
|
||||
"de": "Branch is already up-to-date with origin/master.",
|
||||
"en": "Branch is already up-to-date with origin/master.",
|
||||
"pl": "Branch is already up-to-date with origin/master.",
|
||||
"ru": "Branch is already up-to-date with origin/master.",
|
||||
"zh": "Branch is already up-to-date with origin/master."
|
||||
},
|
||||
"Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR.": {
|
||||
"bg": "Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR.",
|
||||
"de": "Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR.",
|
||||
"en": "Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR.",
|
||||
"pl": "Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR.",
|
||||
"ru": "Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR.",
|
||||
"zh": "Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR."
|
||||
},
|
||||
"Branch is behind origin/master. Rebase first: git fetch origin master && git rebase origin/master": {
|
||||
"bg": "Branch is behind origin/master. Rebase first: git fetch origin master && git rebase origin/master",
|
||||
"de": "Branch is behind origin/master. Rebase first: git fetch origin master && git rebase origin/master",
|
||||
@@ -783,6 +903,14 @@
|
||||
"ru": "Branch is behind origin/master. Rebase first: git fetch origin master && git rebase origin/master",
|
||||
"zh": "Branch is behind origin/master. Rebase first: git fetch origin master && git rebase origin/master"
|
||||
},
|
||||
"Branch is {count} commit(s) behind master. Rebasing...": {
|
||||
"bg": "Branch is {count} commit(s) behind master. Rebasing...",
|
||||
"de": "Branch is {count} commit(s) behind master. Rebasing...",
|
||||
"en": "Branch is {count} commit(s) behind master. Rebasing...",
|
||||
"pl": "Branch is {count} commit(s) behind master. Rebasing...",
|
||||
"ru": "Branch is {count} commit(s) behind master. Rebasing...",
|
||||
"zh": "Branch is {count} commit(s) behind master. Rebasing..."
|
||||
},
|
||||
"Branch name (e.g., DEVX-256-fix-foo)": {
|
||||
"bg": "Branch name (e.g., DEVX-256-fix-foo)",
|
||||
"de": "Branch name (e.g., DEVX-256-fix-foo)",
|
||||
@@ -847,6 +975,14 @@
|
||||
"ru": "CI_GITEA_TOKEN is not set.",
|
||||
"zh": "CI_GITEA_TOKEN is not set."
|
||||
},
|
||||
"CI_GITEA_TOKEN is not set. Add it to .env or export it.": {
|
||||
"bg": "CI_GITEA_TOKEN is not set. Add it to .env or export it.",
|
||||
"de": "CI_GITEA_TOKEN is not set. Add it to .env or export it.",
|
||||
"en": "CI_GITEA_TOKEN is not set. Add it to .env or export it.",
|
||||
"pl": "CI_GITEA_TOKEN is not set. Add it to .env or export it.",
|
||||
"ru": "CI_GITEA_TOKEN is not set. Add it to .env or export it.",
|
||||
"zh": "CI_GITEA_TOKEN is not set. Add it to .env or export it."
|
||||
},
|
||||
"CI_GITEA_TOKEN is not set. Required to create a PR.": {
|
||||
"bg": "CI_GITEA_TOKEN не е зададен. Необходим за създаване на PR.",
|
||||
"de": "CI_GITEA_TOKEN nicht gesetzt. Erforderlich zum Erstellen eines PR.",
|
||||
@@ -863,6 +999,22 @@
|
||||
"ru": "CI_GITEA_TOKEN not set — skipping login configuration.",
|
||||
"zh": "CI_GITEA_TOKEN not set — skipping login configuration."
|
||||
},
|
||||
"Cannot read __version__ from src/{pkg}/__init__.py — skipping.": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Cannot read __version__ from src/{pkg}/__init__.py — skipping.",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Cannot rebase: not on a branch (detached HEAD).": {
|
||||
"bg": "Cannot rebase: not on a branch (detached HEAD).",
|
||||
"de": "Cannot rebase: not on a branch (detached HEAD).",
|
||||
"en": "Cannot rebase: not on a branch (detached HEAD).",
|
||||
"pl": "Cannot rebase: not on a branch (detached HEAD).",
|
||||
"ru": "Cannot rebase: not on a branch (detached HEAD).",
|
||||
"zh": "Cannot rebase: not on a branch (detached HEAD)."
|
||||
},
|
||||
"Checking CLI command documentation...": {
|
||||
"bg": "Checking CLI command documentation...",
|
||||
"de": "Checking CLI command documentation...",
|
||||
@@ -871,6 +1023,14 @@
|
||||
"ru": "Checking CLI command documentation...",
|
||||
"zh": "Checking CLI command documentation..."
|
||||
},
|
||||
"Checking code block languages...": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Checking code block languages...",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Checking docs structure...": {
|
||||
"bg": "Checking docs structure...",
|
||||
"de": "Checking docs structure...",
|
||||
@@ -895,6 +1055,14 @@
|
||||
"ru": "Checking for TODO/FIXME markers...",
|
||||
"zh": "Checking for TODO/FIXME markers..."
|
||||
},
|
||||
"Checking for orphan docs...": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Checking for orphan docs...",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Checking for stale docs...": {
|
||||
"bg": "Checking for stale docs...",
|
||||
"de": "Checking for stale docs...",
|
||||
@@ -919,6 +1087,22 @@
|
||||
"ru": "Checking internal links...",
|
||||
"zh": "Checking internal links..."
|
||||
},
|
||||
"Checking line length...": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Checking line length...",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Checking max heading depth...": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Checking max heading depth...",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Checking required files...": {
|
||||
"bg": "Checking required files...",
|
||||
"de": "Checking required files...",
|
||||
@@ -927,6 +1111,14 @@
|
||||
"ru": "Checking required files...",
|
||||
"zh": "Checking required files..."
|
||||
},
|
||||
"Checking single H1 per file...": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Checking single H1 per file...",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Checking status for PR #{pr_number}...": {
|
||||
"bg": "Checking status for PR #{pr_number}...",
|
||||
"de": "Checking status for PR #{pr_number}...",
|
||||
@@ -943,6 +1135,30 @@
|
||||
"ru": "Checking trailing whitespace...",
|
||||
"zh": "Checking trailing whitespace..."
|
||||
},
|
||||
"Checking version references for {pkg} (current: v{version})": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Checking version references for {pkg} (current: v{version})",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Cloned existing wiki.": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Cloned existing wiki.",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Cloning wiki repo...": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Cloning wiki repo...",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Command failed ({cmd}): {stderr}": {
|
||||
"bg": "Command failed ({cmd}): {stderr}",
|
||||
"de": "Command failed ({cmd}): {stderr}",
|
||||
@@ -967,6 +1183,14 @@
|
||||
"ru": "Commit: {sha}",
|
||||
"zh": "Commit: {sha}"
|
||||
},
|
||||
"Committing and pushing...": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Committing and pushing...",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Comparing {base}..{head} ({count} files changed)": {
|
||||
"bg": "Comparing {base}..{head} ({count} files changed)",
|
||||
"de": "Comparing {base}..{head} ({count} files changed)",
|
||||
@@ -1015,6 +1239,14 @@
|
||||
"ru": "Configuring tea login '{name}' for {url}...",
|
||||
"zh": "Configuring tea login '{name}' for {url}..."
|
||||
},
|
||||
"Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR.": {
|
||||
"bg": "Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR.",
|
||||
"de": "Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR.",
|
||||
"en": "Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR.",
|
||||
"pl": "Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR.",
|
||||
"ru": "Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR.",
|
||||
"zh": "Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR."
|
||||
},
|
||||
"Could not detect current branch: {error}": {
|
||||
"bg": "Не може да се определи текущия клон: {error}",
|
||||
"de": "Aktueller Branch konnte nicht erkannt werden: {error}",
|
||||
@@ -1031,6 +1263,14 @@
|
||||
"ru": "Could not determine head SHA for PR #{pr_number}.",
|
||||
"zh": "Could not determine head SHA for PR #{pr_number}."
|
||||
},
|
||||
"Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables.": {
|
||||
"bg": "Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables.",
|
||||
"de": "Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables.",
|
||||
"en": "Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables.",
|
||||
"pl": "Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables.",
|
||||
"ru": "Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables.",
|
||||
"zh": "Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables."
|
||||
},
|
||||
"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.",
|
||||
@@ -1119,6 +1359,14 @@
|
||||
"ru": "Dependencies must have documentation comments.",
|
||||
"zh": "Dependencies must have documentation comments."
|
||||
},
|
||||
"Directory to scan (default: tests/integration). Can be repeated.": {
|
||||
"en": "Directory to scan (default: tests/integration). Can be repeated.",
|
||||
"bg": "Директория за сканиране (по подразбиране: tests/integration). Може да се повтаря.",
|
||||
"de": "Zu scannendes Verzeichnis (Standard: tests/integration). Kann wiederholt werden.",
|
||||
"pl": "Katalog do skanowania (domyślnie: tests/integration). Można powtarzać.",
|
||||
"ru": "Директория для сканирования (по умолчанию: tests/integration). Можно повторять.",
|
||||
"zh": "要扫描的目录(默认:tests/integration)。可重复。"
|
||||
},
|
||||
"Docker daemon already running": {
|
||||
"bg": "Докер демонът вече работи",
|
||||
"de": "Docker-Daemon läuft bereits",
|
||||
@@ -1207,6 +1455,22 @@
|
||||
"ru": "Каждый элемент должен быть строкой или объектом с 'id', получено {type}",
|
||||
"zh": "每个元素必须是字符串或带有 'id' 的对象,得到 {type}"
|
||||
},
|
||||
"Ensuring standard labels...": {
|
||||
"bg": "Ensuring standard labels...",
|
||||
"de": "Ensuring standard labels...",
|
||||
"en": "Ensuring standard labels...",
|
||||
"pl": "Ensuring standard labels...",
|
||||
"ru": "Ensuring standard labels...",
|
||||
"zh": "Ensuring standard labels..."
|
||||
},
|
||||
"FAIL: Could not clone wiki for verification.": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "FAIL: Could not clone wiki for verification.",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"FAIL: {n} documentation issues found:": {
|
||||
"bg": "FAIL: {n} documentation issues found:",
|
||||
"de": "FAIL: {n} documentation issues found:",
|
||||
@@ -1263,6 +1527,30 @@
|
||||
"ru": "Failed to list versions for {name}: {error}",
|
||||
"zh": "Failed to list versions for {name}: {error}"
|
||||
},
|
||||
"Failed to push release commit after 3 attempts. Manual intervention required.": {
|
||||
"bg": "Failed to push release commit after 3 attempts. Manual intervention required.",
|
||||
"de": "Failed to push release commit after 3 attempts. Manual intervention required.",
|
||||
"en": "Failed to push release commit after 3 attempts. Manual intervention required.",
|
||||
"pl": "Failed to push release commit after 3 attempts. Manual intervention required.",
|
||||
"ru": "Failed to push release commit after 3 attempts. Manual intervention required.",
|
||||
"zh": "Failed to push release commit after 3 attempts. Manual intervention required."
|
||||
},
|
||||
"Failed to start ssh-agent: {error}": {
|
||||
"en": "Failed to start ssh-agent: {error}",
|
||||
"bg": "Неуспешно стартиране на ssh-agent: {error}",
|
||||
"de": "Starten von ssh-agent fehlgeschlagen: {error}",
|
||||
"pl": "Nie udało się uruchomić ssh-agent: {error}",
|
||||
"ru": "Не удалось запустить ssh-agent: {error}",
|
||||
"zh": "启动 ssh-agent 失败: {error}"
|
||||
},
|
||||
"Fetch failed: {error}": {
|
||||
"bg": "Fetch failed: {error}",
|
||||
"de": "Fetch failed: {error}",
|
||||
"en": "Fetch failed: {error}",
|
||||
"pl": "Fetch failed: {error}",
|
||||
"ru": "Fetch failed: {error}",
|
||||
"zh": "Fetch failed: {error}"
|
||||
},
|
||||
"Fetching logs for PR #{pr_number}...": {
|
||||
"bg": "Fetching logs for PR #{pr_number}...",
|
||||
"de": "Fetching logs for PR #{pr_number}...",
|
||||
@@ -1271,13 +1559,29 @@
|
||||
"ru": "Fetching logs for PR #{pr_number}...",
|
||||
"zh": "Fetching logs for PR #{pr_number}..."
|
||||
},
|
||||
"Found {count} existing wiki pages.": {
|
||||
"bg": "Found {count} existing wiki pages.",
|
||||
"de": "Found {count} existing wiki pages.",
|
||||
"en": "Found {count} existing wiki pages.",
|
||||
"pl": "Znaleziono {count} istniejących stron wiki.",
|
||||
"ru": "Found {count} existing wiki pages.",
|
||||
"zh": "Found {count} existing wiki pages."
|
||||
"Fetching origin/master...": {
|
||||
"bg": "Fetching origin/master...",
|
||||
"de": "Fetching origin/master...",
|
||||
"en": "Fetching origin/master...",
|
||||
"pl": "Fetching origin/master...",
|
||||
"ru": "Fetching origin/master...",
|
||||
"zh": "Fetching origin/master..."
|
||||
},
|
||||
"Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again.": {
|
||||
"bg": "Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again.",
|
||||
"de": "Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again.",
|
||||
"en": "Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again.",
|
||||
"pl": "Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again.",
|
||||
"ru": "Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again.",
|
||||
"zh": "Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again."
|
||||
},
|
||||
"Force-pushing...": {
|
||||
"bg": "Force-pushing...",
|
||||
"de": "Force-pushing...",
|
||||
"en": "Force-pushing...",
|
||||
"pl": "Force-pushing...",
|
||||
"ru": "Force-pushing...",
|
||||
"zh": "Force-pushing..."
|
||||
},
|
||||
"Found {count} mutable global(s) — use factory functions or pytest fixtures.": {
|
||||
"bg": "Found {count} mutable global(s) — use factory functions or pytest fixtures.",
|
||||
@@ -1295,6 +1599,14 @@
|
||||
"ru": "Found {count} stale documentation reference(s)",
|
||||
"zh": "Found {count} stale documentation reference(s)"
|
||||
},
|
||||
"Found {count} unsafe identity check(s) in integration tests.": {
|
||||
"en": "Found {count} unsafe identity check(s) in integration tests.",
|
||||
"bg": "Намерени са {count} небрежни проверки за идентичност в интеграционните тестове.",
|
||||
"de": "{count} unsichere Identitätsprüfung(en) in Integrationstests gefunden.",
|
||||
"pl": "Znaleziono {count} niebezpiecznych sprawdzeń tożsamości w testach integracyjnych.",
|
||||
"ru": "Найдено {count} небезопасных проверок идентичности в интеграционных тестах.",
|
||||
"zh": "在集成测试中发现 {count} 个不安全的身份检查。"
|
||||
},
|
||||
"Found {count} version(s):": {
|
||||
"bg": "Found {count} version(s):",
|
||||
"de": "Found {count} version(s):",
|
||||
@@ -1519,6 +1831,14 @@
|
||||
"ru": "Linting documentation in {root}...",
|
||||
"zh": "Linting documentation in {root}..."
|
||||
},
|
||||
"Login to {registry} failed: {error}": {
|
||||
"en": "Login to {registry} failed: {error}",
|
||||
"bg": "Влизането в {registry} не успя: {error}",
|
||||
"de": "Anmeldung bei {registry} fehlgeschlagen: {error}",
|
||||
"pl": "Logowanie do {registry} nie powiodło się: {error}",
|
||||
"ru": "Ошибка входа в {registry}: {error}",
|
||||
"zh": "登录 {registry} 失败: {error}"
|
||||
},
|
||||
"Manifest file not found: {path}": {
|
||||
"bg": "Manifest file not found: {path}",
|
||||
"de": "Manifest file not found: {path}",
|
||||
@@ -1535,22 +1855,6 @@
|
||||
"ru": "Manifest must be a JSON list",
|
||||
"zh": "Manifest must be a JSON list"
|
||||
},
|
||||
"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.",
|
||||
"en": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
|
||||
"pl": "Mapowany plik {file} jest pusty. Zaktualizuj treść lub usuń z 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.": {
|
||||
"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.",
|
||||
"en": "Mapped file {file} not found. Update mapping.json or create the file.",
|
||||
"pl": "Mapowany plik {file} nie znaleziony. Zaktualizuj mapping.json lub utwórz plik.",
|
||||
"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 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.",
|
||||
@@ -1631,6 +1935,14 @@
|
||||
"ru": "No CI checks found for commit {sha}.",
|
||||
"zh": "No CI checks found for commit {sha}."
|
||||
},
|
||||
"No Python package found under src/ — skipping version check.": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "No Python package found under src/ — skipping version check.",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"No badge SVG files generated": {
|
||||
"bg": "No badge SVG files generated",
|
||||
"de": "No badge SVG files generated",
|
||||
@@ -1647,6 +1959,14 @@
|
||||
"ru": "No badge URLs found to update — README already up to date",
|
||||
"zh": "No badge URLs found to update — README already up to date"
|
||||
},
|
||||
"No badge changes — skipping commit": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "No badge changes — skipping commit",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"No changes between {base} and {head}.": {
|
||||
"bg": "No changes between {base} and {head}.",
|
||||
"de": "No changes between {base} and {head}.",
|
||||
@@ -1655,6 +1975,14 @@
|
||||
"ru": "No changes between {base} and {head}.",
|
||||
"zh": "No changes between {base} and {head}."
|
||||
},
|
||||
"No changes to sync — wiki is up to date.": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "No changes to sync — wiki is up to date.",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"No failed jobs.": {
|
||||
"bg": "No failed jobs.",
|
||||
"de": "No failed jobs.",
|
||||
@@ -1687,6 +2015,14 @@
|
||||
"ru": "No open PR found for branch '{branch}'.",
|
||||
"zh": "No open PR found for branch '{branch}'."
|
||||
},
|
||||
"No push needed (no changes or push failed).": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "No push needed (no changes or push failed).",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"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.",
|
||||
@@ -1767,6 +2103,14 @@
|
||||
"ru": "Note: Self-approval not allowed. Posting COMMENT instead.",
|
||||
"zh": "Note: Self-approval not allowed. Posting COMMENT instead."
|
||||
},
|
||||
"Nothing to push.": {
|
||||
"bg": "Nothing to push.",
|
||||
"de": "Nothing to push.",
|
||||
"en": "Nothing to push.",
|
||||
"pl": "Nothing to push.",
|
||||
"ru": "Nothing to push.",
|
||||
"zh": "Nothing to push."
|
||||
},
|
||||
"Only check staged files (for pre-commit)": {
|
||||
"bg": "Only check staged files (for pre-commit)",
|
||||
"de": "Only check staged files (for pre-commit)",
|
||||
@@ -1871,6 +2215,14 @@
|
||||
"ru": "PASSED: {pair}",
|
||||
"zh": "PASSED: {pair}"
|
||||
},
|
||||
"PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR.": {
|
||||
"bg": "PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR.",
|
||||
"de": "PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR.",
|
||||
"en": "PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR.",
|
||||
"pl": "PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR.",
|
||||
"ru": "PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR.",
|
||||
"zh": "PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR."
|
||||
},
|
||||
"PR already exists: #{index} — {url}": {
|
||||
"bg": "PR вече съществува: #{index} — {url}",
|
||||
"de": "PR existiert bereits: #{index} — {url}",
|
||||
@@ -2047,6 +2399,14 @@
|
||||
"ru": "Publishing release {tag}...",
|
||||
"zh": "Publishing release {tag}..."
|
||||
},
|
||||
"Push attempt {n}/3 failed: {err}": {
|
||||
"bg": "Push attempt {n}/3 failed: {err}",
|
||||
"de": "Push attempt {n}/3 failed: {err}",
|
||||
"en": "Push attempt {n}/3 failed: {err}",
|
||||
"pl": "Push attempt {n}/3 failed: {err}",
|
||||
"ru": "Push attempt {n}/3 failed: {err}",
|
||||
"zh": "Push attempt {n}/3 failed: {err}"
|
||||
},
|
||||
"Push failed for {tag}: {error}": {
|
||||
"bg": "Push failed for {tag}: {error}",
|
||||
"de": "Push failed for {tag}: {error}",
|
||||
@@ -2055,6 +2415,14 @@
|
||||
"ru": "Push failed for {tag}: {error}",
|
||||
"zh": "Push failed for {tag}: {error}"
|
||||
},
|
||||
"Push failed: {error}": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Push failed: {error}",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Pushed README update with badge SHA {sha}": {
|
||||
"bg": "Pushed README update with badge SHA {sha}",
|
||||
"de": "Pushed README update with badge SHA {sha}",
|
||||
@@ -2071,6 +2439,14 @@
|
||||
"ru": "Pushed release commit to master.",
|
||||
"zh": "Pushed release commit to master."
|
||||
},
|
||||
"Pushed {branch} to origin.": {
|
||||
"bg": "Pushed {branch} to origin.",
|
||||
"de": "Pushed {branch} to origin.",
|
||||
"en": "Pushed {branch} to origin.",
|
||||
"pl": "Pushed {branch} to origin.",
|
||||
"ru": "Pushed {branch} to origin.",
|
||||
"zh": "Pushed {branch} to origin."
|
||||
},
|
||||
"PyPI publish failed (non-fatal — continuing to Gitea release):\n{error}": {
|
||||
"bg": "Публикуването в PyPI неуспешно (некритично — продължава към Gitea release):\n{error}",
|
||||
"de": "PyPI-Veröffentlichung fehlgeschlagen (nicht fatal — Gitea-Release wird fortgesetzt):\n{error}",
|
||||
@@ -2087,6 +2463,46 @@
|
||||
"ru": "REPO argument is required (or set GITHUB_REPOSITORY env var).",
|
||||
"zh": "REPO argument is required (or set GITHUB_REPOSITORY env var)."
|
||||
},
|
||||
"Rebase attempt {n}/3 failed: {err}": {
|
||||
"bg": "Rebase attempt {n}/3 failed: {err}",
|
||||
"de": "Rebase attempt {n}/3 failed: {err}",
|
||||
"en": "Rebase attempt {n}/3 failed: {err}",
|
||||
"pl": "Rebase attempt {n}/3 failed: {err}",
|
||||
"ru": "Rebase attempt {n}/3 failed: {err}",
|
||||
"zh": "Rebase attempt {n}/3 failed: {err}"
|
||||
},
|
||||
"Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue": {
|
||||
"bg": "Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue",
|
||||
"de": "Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue",
|
||||
"en": "Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue",
|
||||
"pl": "Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue",
|
||||
"ru": "Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue",
|
||||
"zh": "Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue"
|
||||
},
|
||||
"Rebase failed with HTTP {status}: {message}": {
|
||||
"bg": "Rebase failed with HTTP {status}: {message}",
|
||||
"de": "Rebase failed with HTTP {status}: {message}",
|
||||
"en": "Rebase failed with HTTP {status}: {message}",
|
||||
"pl": "Rebase failed with HTTP {status}: {message}",
|
||||
"ru": "Rebase failed with HTTP {status}: {message}",
|
||||
"zh": "Rebase failed with HTTP {status}: {message}"
|
||||
},
|
||||
"Rebase successful.": {
|
||||
"bg": "Rebase successful.",
|
||||
"de": "Rebase successful.",
|
||||
"en": "Rebase successful.",
|
||||
"pl": "Rebase successful.",
|
||||
"ru": "Rebase successful.",
|
||||
"zh": "Rebase successful."
|
||||
},
|
||||
"Rebasing PR #{pr} via Gitea API...": {
|
||||
"bg": "Rebasing PR #{pr} via Gitea API...",
|
||||
"de": "Rebasing PR #{pr} via Gitea API...",
|
||||
"en": "Rebasing PR #{pr} via Gitea API...",
|
||||
"pl": "Rebasing PR #{pr} via Gitea API...",
|
||||
"ru": "Rebasing PR #{pr} via Gitea API...",
|
||||
"zh": "Rebasing PR #{pr} via Gitea API..."
|
||||
},
|
||||
"Registry credentials required: set CI_GITEA_TOKEN and CI_GITEA_USERNAME env vars": {
|
||||
"bg": "Registry credentials required: set CI_GITEA_TOKEN and CI_GITEA_USERNAME env vars",
|
||||
"de": "Registry credentials required: set CI_GITEA_TOKEN and CI_GITEA_USERNAME env vars",
|
||||
@@ -2127,14 +2543,6 @@
|
||||
"ru": "Release commit — skipping all post-merge jobs.",
|
||||
"zh": "Release commit — skipping all post-merge jobs."
|
||||
},
|
||||
"Automated CI commit (badge) — skipping post-merge jobs.": {
|
||||
"bg": "Automated CI commit (badge) — skipping post-merge jobs.",
|
||||
"de": "Automated CI commit (badge) — skipping post-merge jobs.",
|
||||
"en": "Automated CI commit (badge) — skipping post-merge jobs.",
|
||||
"pl": "Automated CI commit (badge) — skipping post-merge jobs.",
|
||||
"ru": "Automated CI commit (badge) — skipping post-merge jobs.",
|
||||
"zh": "Automated CI commit (badge) — skipping post-merge jobs."
|
||||
},
|
||||
"Release creation failed: {error}": {
|
||||
"bg": "Release creation failed: {error}",
|
||||
"de": "Release creation failed: {error}",
|
||||
@@ -2191,6 +2599,14 @@
|
||||
"ru": "Владелец репозитория не установлен. Используйте --owner или DEVX_REPO_OWNER env var.",
|
||||
"zh": "仓库所有者未设置。使用 --owner 或 DEVX_REPO_OWNER 环境变量。"
|
||||
},
|
||||
"Required tools missing.": {
|
||||
"en": "Required tools missing.",
|
||||
"bg": "Липсват задължителни инструменти.",
|
||||
"de": "Erforderliche Werkzeuge fehlen.",
|
||||
"pl": "Brak wymaganych narzędzi.",
|
||||
"ru": "Отсутствуют обязательные инструменты.",
|
||||
"zh": "缺少必需的工具。"
|
||||
},
|
||||
"Review body must be at least 50 characters.": {
|
||||
"bg": "Review body must be at least 50 characters.",
|
||||
"de": "Review body must be at least 50 characters.",
|
||||
@@ -2279,6 +2695,30 @@
|
||||
"ru": "Running: {scenario} on {platform}",
|
||||
"zh": "Running: {scenario} on {platform}"
|
||||
},
|
||||
"SSH key set up successfully": {
|
||||
"en": "SSH key set up successfully",
|
||||
"bg": "SSH ключът е настроен успешно",
|
||||
"de": "SSH-Schlüssel erfolgreich eingerichtet",
|
||||
"pl": "Klucz SSH skonfigurowany pomyślnie",
|
||||
"ru": "SSH-ключ успешно настроен",
|
||||
"zh": "SSH 密钥设置成功"
|
||||
},
|
||||
"SSH key setup skipped (no key provided)": {
|
||||
"en": "SSH key setup skipped (no key provided)",
|
||||
"bg": "Настройката на SSH ключ е пропусната (не е предоставен ключ)",
|
||||
"de": "SSH-Schlüssel-Setup übersprungen (kein Schlüssel bereitgestellt)",
|
||||
"pl": "Pominięto konfigurację klucza SSH (brak klucza)",
|
||||
"ru": "Настройка SSH-ключа пропущена (ключ не предоставлен)",
|
||||
"zh": "SSH 密钥设置已跳过(未提供密钥)"
|
||||
},
|
||||
"SSH_PRIVATE_KEY not set — skipping SSH key setup": {
|
||||
"en": "SSH_PRIVATE_KEY not set — skipping SSH key setup",
|
||||
"bg": "SSH_PRIVATE_KEY не е зададен — пропускане на SSH ключ настройката",
|
||||
"de": "SSH_PRIVATE_KEY nicht gesetzt — SSH-Schlüssel-Setup übersprungen",
|
||||
"pl": "SSH_PRIVATE_KEY nie ustawione — pomijanie konfiguracji klucza SSH",
|
||||
"ru": "SSH_PRIVATE_KEY не задан — пропуск настройки SSH-ключа",
|
||||
"zh": "SSH_PRIVATE_KEY 未设置 — 跳过 SSH 密钥设置"
|
||||
},
|
||||
"Skip Vikunja title match check": {
|
||||
"bg": "Skip Vikunja title match check",
|
||||
"de": "Skip Vikunja title match check",
|
||||
@@ -2319,13 +2759,21 @@
|
||||
"ru": "Synced to latest origin/{branch}",
|
||||
"zh": "Synced to latest origin/{branch}"
|
||||
},
|
||||
"Syncing {count} documentation pages to wiki...": {
|
||||
"bg": "Syncing {count} documentation pages to wiki...",
|
||||
"de": "Syncing {count} documentation pages to wiki...",
|
||||
"en": "Syncing {count} documentation pages to wiki...",
|
||||
"pl": "Synchronizowanie {count} stron dokumentacji do wiki...",
|
||||
"ru": "Syncing {count} documentation pages to wiki...",
|
||||
"zh": "Syncing {count} documentation pages to wiki..."
|
||||
"Syncing files...": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Syncing files...",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Syncing {count} documentation pages to wiki via Git...": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Syncing {count} documentation pages to wiki via Git...",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Tag consistency check failed.": {
|
||||
"bg": "Tag consistency check failed.",
|
||||
@@ -2439,6 +2887,14 @@
|
||||
"ru": "Updated badge URLs in {filename}",
|
||||
"zh": "Updated badge URLs in {filename}"
|
||||
},
|
||||
"Updated documentation version references to v{version}": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Updated documentation version references to v{version}",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Updated version in {init}": {
|
||||
"bg": "Updated version in {init}",
|
||||
"de": "Updated version in {init}",
|
||||
@@ -2455,6 +2911,14 @@
|
||||
"ru": "Updated {changelog_file}",
|
||||
"zh": "Updated {changelog_file}"
|
||||
},
|
||||
"Use string comparison or _is_truthy()/_is_falsy() helpers instead. Add '{marker}' to suppress individual lines.": {
|
||||
"en": "Use string comparison or _is_truthy()/_is_falsy() helpers instead. Add '{marker}' to suppress individual lines.",
|
||||
"bg": "Използвайте сравнение на низове или _is_truthy()/_is_falsy() помощници. Добавете '{marker}' за потискане на отделни редове.",
|
||||
"de": "Verwenden Sie String-Vergleich oder _is_truthy()/_is_falsy() Hilfsfunktionen. Fügen Sie '{marker}' hinzu, um einzelne Zeilen zu unterdrücken.",
|
||||
"pl": "Użyj porównania ciągów lub pomocników _is_truthy()/_is_falsy(). Dodaj '{marker}', aby pominąć pojedyncze linie.",
|
||||
"ru": "Используйте строковое сравнение или помощники _is_truthy()/_is_falsy(). Добавьте '{marker}' для подавления отдельных строк.",
|
||||
"zh": "使用字符串比较或 _is_truthy()/_is_falsy() 辅助函数。添加 '{marker}' 以抑制个别行。"
|
||||
},
|
||||
"VIKUNJA_TOKEN is not set. Required to derive PR title.": {
|
||||
"bg": "VIKUNJA_TOKEN не е зададен. Необходим за извличане на PR заглавие.",
|
||||
"de": "VIKUNJA_TOKEN nicht gesetzt. Erforderlich zum Ableiten des PR-Titels.",
|
||||
@@ -2511,6 +2975,38 @@
|
||||
"ru": "Задача Vikunja {task_id} не найдена в проекте {project_id}.\n Сначала создайте её:\n python -m devx.tools.create_task --title \"Заголовок задачи\"\n Или проверьте, что ID задачи в имени ветки корректен.",
|
||||
"zh": "在项目 {project_id} 中找不到 Vikunja 任务 {task_id}。\n 请先创建:\n python -m devx.tools.create_task --title \"任务标题\"\n 或检查分支名称中的任务 ID 是否正确。"
|
||||
},
|
||||
"WARN: .venv has Python {version}, but >={req} is required.": {
|
||||
"en": "WARN: .venv has Python {version}, but >={req} is required.",
|
||||
"bg": "ПРЕДУПРЕЖДЕНИЕ: .venv има Python {version}, но се изисква >={req}.",
|
||||
"de": "WARNUNG: .venv hat Python {version}, aber >={req} ist erforderlich.",
|
||||
"pl": "OSTRZEŻENIE: .venv ma Python {version}, ale wymagane jest >={req}.",
|
||||
"ru": "ПРЕДУПРЕЖДЕНИЕ: в .venv установлен Python {version}, но требуется >={req}.",
|
||||
"zh": "警告: .venv 的 Python 版本为 {version},但要求 >={req}。"
|
||||
},
|
||||
"WARN: .venv not found. Run 'make setup-venv' to create it.": {
|
||||
"en": "WARN: .venv not found. Run 'make setup-venv' to create it.",
|
||||
"bg": "ПРЕДУПРЕЖДЕНИЕ: .venv не е намерен. Изпълнете 'make setup-venv' за създаване.",
|
||||
"de": "WARNUNG: .venv nicht gefunden. Führen Sie 'make setup-venv' aus, um es zu erstellen.",
|
||||
"pl": "OSTRZEŻENIE: Nie znaleziono .venv. Uruchom 'make setup-venv', aby utworzyć.",
|
||||
"ru": "ПРЕДУПРЕЖДЕНИЕ: .venv не найден. Выполните 'make setup-venv' для создания.",
|
||||
"zh": "警告: 未找到 .venv。运行 'make setup-venv' 来创建。"
|
||||
},
|
||||
"WARN: Could not determine Python version in .venv.": {
|
||||
"en": "WARN: Could not determine Python version in .venv.",
|
||||
"bg": "ПРЕДУПРЕЖДЕНИЕ: Не може да се определи версията на Python в .venv.",
|
||||
"de": "WARNUNG: Python-Version in .venv konnte nicht bestimmt werden.",
|
||||
"pl": "OSTRZEŻENIE: Nie można określić wersji Python w .venv.",
|
||||
"ru": "ПРЕДУПРЕЖДЕНИЕ: Не удалось определить версию Python в .venv.",
|
||||
"zh": "警告: 无法确定 .venv 中的 Python 版本。"
|
||||
},
|
||||
"WARN: Could not parse Python version '{version}'.": {
|
||||
"en": "WARN: Could not parse Python version '{version}'.",
|
||||
"bg": "ПРЕДУПРЕЖДЕНИЕ: Не може да се анализира версията на Python '{version}'.",
|
||||
"de": "WARNUNG: Python-Version '{version}' konnte nicht analysiert werden.",
|
||||
"pl": "OSTRZEŻENIE: Nie można przeanalizować wersji Python '{version}'.",
|
||||
"ru": "ПРЕДУПРЕЖДЕНИЕ: Не удалось разобрать версию Python '{version}'.",
|
||||
"zh": "警告: 无法解析 Python 版本 '{version}'。"
|
||||
},
|
||||
"WARNING: --skip-tests passed — skipping test verification.": {
|
||||
"bg": "WARNING: --skip-tests passed — skipping test verification.",
|
||||
"de": "WARNING: --skip-tests passed — skipping test verification.",
|
||||
@@ -2527,22 +3023,6 @@
|
||||
"ru": "ВНИМАНИЕ: Файл .taskid ({file_id}) устарел и не совпадает с именем ветки ({branch_id}). Удалите .taskid из репозитория — имя ветки — единственный источник истины.",
|
||||
"zh": "警告:.taskid 文件 ({file_id}) 已弃用,与分支名称 ({branch_id}) 不一致。请从仓库中删除 .taskid — 分支名称是唯一的真实来源。"
|
||||
},
|
||||
"WARNING: Could not fetch wiki page list after retries. The sync itself succeeded ({count} pages updated), but the integrity check could not verify them due to a transient API issue.": {
|
||||
"bg": "WARNING: Could not fetch wiki page list after retries. The sync itself succeeded ({count} pages updated), but the integrity check could not verify them due to a transient API issue.",
|
||||
"de": "WARNING: Could not fetch wiki page list after retries. The sync itself succeeded ({count} pages updated), but the integrity check could not verify them due to a transient API issue.",
|
||||
"en": "WARNING: Could not fetch wiki page list after retries. The sync itself succeeded ({count} pages updated), but the integrity check could not verify them due to a transient API issue.",
|
||||
"pl": "OSTRZEŻENIE: Nie można pobrać listy stron wiki po ponownych próbach. Sama synchronizacja zakończyła się sukcesem (zaktualizowano {count} stron), ale kontrola integralności nie mogła ich zweryfikować z powodu przejściowego problemu z API.",
|
||||
"ru": "WARNING: Could not fetch wiki page list after retries. The sync itself succeeded ({count} pages updated), but the integrity check could not verify them due to a transient API issue.",
|
||||
"zh": "WARNING: Could not fetch wiki page list after retries. The sync itself succeeded ({count} pages updated), but the integrity check could not verify them due to a transient API issue."
|
||||
},
|
||||
"WARNING: Could not re-fetch wiki page list for verification. Skipping content verification due to transient API issue.": {
|
||||
"bg": "WARNING: Could not re-fetch wiki page list for verification. Skipping content verification due to transient API issue.",
|
||||
"de": "WARNING: Could not re-fetch wiki page list for verification. Skipping content verification due to transient API issue.",
|
||||
"en": "WARNING: Could not re-fetch wiki page list for verification. Skipping content verification due to transient API issue.",
|
||||
"pl": "OSTRZEŻENIE: Nie można ponownie pobrać listy stron wiki do weryfikacji. Pomijanie weryfikacji treści z powodu przejściowego problemu z API.",
|
||||
"ru": "WARNING: Could not re-fetch wiki page list for verification. Skipping content verification due to transient API issue.",
|
||||
"zh": "WARNING: Could not re-fetch wiki page list for verification. Skipping content verification due to transient API issue."
|
||||
},
|
||||
"WARNING: VIKUNJA_TOKEN not set — skipping task existence check. Set it in .env to enable full validation.": {
|
||||
"bg": "ПРЕДУПРЕЖДЕНИЕ: VIKUNJA_TOKEN не е зададен — пропускане на проверката за съществуване на задача. Задайте го в .env за пълна валидация.",
|
||||
"de": "WARNUNG: VIKUNJA_TOKEN nicht gesetzt — Task-Existenzprüfung übersprungen. In .env setzen für volle Validierung.",
|
||||
@@ -2551,6 +3031,30 @@
|
||||
"ru": "ПРЕДУПРЕЖДЕНИЕ: VIKUNJA_TOKEN не установлен — пропуск проверки существования задачи. Установите в .env для полной проверки.",
|
||||
"zh": "警告: VIKUNJA_TOKEN 未设置 — 跳过任务存在性检查。在 .env 中设置以启用完整验证。"
|
||||
},
|
||||
"WARNING: Version badge shows stale version (expected v{version}) — regenerating": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "WARNING: Version badge shows stale version (expected v{version}) — regenerating",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"WARNING: check_doc_versions --fix failed (rc={rc}): {err}": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "WARNING: check_doc_versions --fix failed (rc={rc}): {err}",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Waiting 5s for Gitea to process pushed commits...": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Waiting 5s for Gitea to process pushed commits...",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Waiting for CI checks to complete (timeout: {timeout}s)...": {
|
||||
"bg": "Waiting for CI checks to complete (timeout: {timeout}s)...",
|
||||
"de": "Waiting for CI checks to complete (timeout: {timeout}s)...",
|
||||
@@ -2615,21 +3119,37 @@
|
||||
"ru": "Warning: repo-level runners query returned HTTP {status}",
|
||||
"zh": "Warning: repo-level runners query returned HTTP {status}"
|
||||
},
|
||||
"Wiki integrity check failed — {count} issue(s)": {
|
||||
"bg": "Wiki integrity check failed — {count} issue(s)",
|
||||
"de": "Wiki integrity check failed — {count} issue(s)",
|
||||
"en": "Wiki integrity check failed — {count} issue(s)",
|
||||
"pl": "Kontrola integralności wiki nie powiodła się — {count} problem(ów)",
|
||||
"ru": "Wiki integrity check failed — {count} issue(s)",
|
||||
"zh": "Wiki integrity check failed — {count} issue(s)"
|
||||
"Wiki repo not found or empty — initializing fresh.": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Wiki repo not found or empty — initializing fresh.",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"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",
|
||||
"en": "Wiki verification failed — {failures} page(s) empty or mismatched",
|
||||
"pl": "Weryfikacja wiki nie powiodła się — {failures} strona(y) pusta lub niezgodna",
|
||||
"ru": "Wiki verification failed — {failures} page(s) empty or mismatched",
|
||||
"zh": "Wiki verification failed — {failures} page(s) empty or mismatched"
|
||||
"Wiki synced successfully.": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Wiki synced successfully.",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Wiki verification failed — could not clone wiki": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Wiki verification failed — could not clone wiki",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Wiki verification failed — {failures} page(s) missing": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "Wiki verification failed — {failures} page(s) missing",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"Wrote tag {tag} to GITHUB_OUTPUT.": {
|
||||
"bg": "Wrote tag {tag} to GITHUB_OUTPUT.",
|
||||
@@ -2639,6 +3159,14 @@
|
||||
"ru": "Wrote tag {tag} to GITHUB_OUTPUT.",
|
||||
"zh": "Wrote tag {tag} to GITHUB_OUTPUT."
|
||||
},
|
||||
"[check-api-identity-checks] Passed: no unsafe identity checks found": {
|
||||
"en": "[check-api-identity-checks] Passed: no unsafe identity checks found",
|
||||
"bg": "[check-api-identity-checks] Мина: не са намерени небрежни проверки за идентичност",
|
||||
"de": "[check-api-identity-checks] Bestanden: keine unsicheren Identitätsprüfungen gefunden",
|
||||
"pl": "[check-api-identity-checks] Passed: nie znaleziono niebezpiecznych sprawdzeń tożsamości",
|
||||
"ru": "[check-api-identity-checks] Пройдено: небезопасных проверок идентичности не найдено",
|
||||
"zh": "[check-api-identity-checks] 通过:未发现不安全的身份检查"
|
||||
},
|
||||
"[check-dep-docs] Passed: all dependencies are documented": {
|
||||
"bg": "[check-dep-docs] Passed: all dependencies are documented",
|
||||
"de": "[check-dep-docs] Passed: all dependencies are documented",
|
||||
@@ -2647,6 +3175,30 @@
|
||||
"ru": "[check-dep-docs] Passed: all dependencies are documented",
|
||||
"zh": "[check-dep-docs] Passed: all dependencies are documented"
|
||||
},
|
||||
"[check-deps] All core tools present.": {
|
||||
"en": "[check-deps] All core tools present.",
|
||||
"bg": "[check-deps] Всички основни инструменти са налични.",
|
||||
"de": "[check-deps] Alle Kernwerkzeuge vorhanden.",
|
||||
"pl": "[check-deps] Wszystkie podstawowe narzędzia są dostępne.",
|
||||
"ru": "[check-deps] Все основные инструменты доступны.",
|
||||
"zh": "[check-deps] 所有核心工具均已就绪。"
|
||||
},
|
||||
"[check-deps] Verifying tools...": {
|
||||
"en": "[check-deps] Verifying tools...",
|
||||
"bg": "[check-deps] Проверка на инструментите...",
|
||||
"de": "[check-deps] Werkzeuge werden überprüft...",
|
||||
"pl": "[check-deps] Sprawdzanie narzędzi...",
|
||||
"ru": "[check-deps] Проверка инструментов...",
|
||||
"zh": "[check-deps] 正在验证工具..."
|
||||
},
|
||||
"[check-deps] Virtualenv .venv ready (Python {version}).": {
|
||||
"en": "[check-deps] Virtualenv .venv ready (Python {version}).",
|
||||
"bg": "[check-deps] Виртуална среда .venv готова (Python {version}).",
|
||||
"de": "[check-deps] Virtuelle Umgebung .venv bereit (Python {version}).",
|
||||
"pl": "[check-deps] Środowisko wirtualne .venv gotowe (Python {version}).",
|
||||
"ru": "[check-deps] Виртуальное окружение .venv готово (Python {version}).",
|
||||
"zh": "[check-deps] 虚拟环境 .venv 已就绪 (Python {version})。"
|
||||
},
|
||||
"[check-mutable-globals] Passed: no mutable path globals found": {
|
||||
"bg": "[check-mutable-globals] Passed: no mutable path globals found",
|
||||
"de": "[check-mutable-globals] Passed: no mutable path globals found",
|
||||
@@ -2671,6 +3223,46 @@
|
||||
"ru": "[check_test_coverage] No changed files to check.",
|
||||
"zh": "[check_test_coverage] No changed files to check."
|
||||
},
|
||||
"[docker-login] Logged in to {registry}.": {
|
||||
"en": "[docker-login] Logged in to {registry}.",
|
||||
"bg": "[docker-login] Влязъл в {registry}.",
|
||||
"de": "[docker-login] Angemeldet bei {registry}.",
|
||||
"pl": "[docker-login] Zalogowano do {registry}.",
|
||||
"ru": "[docker-login] Выполнен вход в {registry}.",
|
||||
"zh": "[docker-login] 已登录到 {registry}。"
|
||||
},
|
||||
"[docker-login] Login to {registry} failed (continuing).": {
|
||||
"en": "[docker-login] Login to {registry} failed (continuing).",
|
||||
"bg": "[docker-login] Влизането в {registry} не успя (продължава).",
|
||||
"de": "[docker-login] Anmeldung bei {registry} fehlgeschlagen (wird fortgesetzt).",
|
||||
"pl": "[docker-login] Logowanie do {registry} nie powiodło się (kontynuowanie).",
|
||||
"ru": "[docker-login] Ошибка входа в {registry} (продолжаем).",
|
||||
"zh": "[docker-login] 登录 {registry} 失败(继续)。"
|
||||
},
|
||||
"[docker-login] Skipping {registry} (token {env} not set).": {
|
||||
"en": "[docker-login] Skipping {registry} (token {env} not set).",
|
||||
"bg": "[docker-login] Пропускане на {registry} (токен {env} не е зададен).",
|
||||
"de": "[docker-login] {registry} übersprungen (Token {env} nicht gesetzt).",
|
||||
"pl": "[docker-login] Pomijanie {registry} (token {env} nie ustawiony).",
|
||||
"ru": "[docker-login] Пропуск {registry} (токен {env} не задан).",
|
||||
"zh": "[docker-login] 跳过 {registry}(未设置令牌 {env})。"
|
||||
},
|
||||
"[dry-run] No changes pushed.": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "[dry-run] No changes pushed.",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"[dry-run] Would commit and push wiki changes": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "[dry-run] Would commit and push wiki changes",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"[dry-run] Would commit: release: v{version} [skip ci]": {
|
||||
"bg": "[dry-run] Would commit: release: v{version} [skip ci]",
|
||||
"de": "[dry-run] Would commit: release: v{version} [skip ci]",
|
||||
@@ -2703,13 +3295,13 @@
|
||||
"ru": "[dry-run] Would push commit to master",
|
||||
"zh": "[dry-run] Would push commit to master"
|
||||
},
|
||||
"[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)",
|
||||
"en": "[dry-run] Would sync page: {title} ({chars} chars)",
|
||||
"pl": "[dry-run] Zsynchronizowano by stronę: {title} ({chars} znaków)",
|
||||
"ru": "[dry-run] Would sync page: {title} ({chars} chars)",
|
||||
"zh": "[dry-run] Would sync page: {title} ({chars} chars)"
|
||||
"[dry-run] Would update doc version references via check_doc_versions --fix": {
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"en": "[dry-run] Would update doc version references via check_doc_versions --fix",
|
||||
"pl": "",
|
||||
"ru": "",
|
||||
"zh": ""
|
||||
},
|
||||
"[dry-run] Would update {changelog_file}": {
|
||||
"bg": "[dry-run] Would update {changelog_file}",
|
||||
@@ -2727,6 +3319,38 @@
|
||||
"ru": "[dry-run] Would update {init}",
|
||||
"zh": "[dry-run] Would update {init}"
|
||||
},
|
||||
"[tofu-init] Done.": {
|
||||
"en": "[tofu-init] Done.",
|
||||
"bg": "[tofu-init] Готово.",
|
||||
"de": "[tofu-init] Fertig.",
|
||||
"pl": "[tofu-init] Gotowe.",
|
||||
"ru": "[tofu-init] Готово.",
|
||||
"zh": "[tofu-init] 完成。"
|
||||
},
|
||||
"[tofu-init] Initializing {dir}...": {
|
||||
"en": "[tofu-init] Initializing {dir}...",
|
||||
"bg": "[tofu-init] Инициализиране на {dir}...",
|
||||
"de": "[tofu-init] Initialisiere {dir}...",
|
||||
"pl": "[tofu-init] Inicjalizacja {dir}...",
|
||||
"ru": "[tofu-init] Инициализация {dir}...",
|
||||
"zh": "[tofu-init] 正在初始化 {dir}..."
|
||||
},
|
||||
"[tofu-{mode}] All configurations valid.": {
|
||||
"en": "[tofu-{mode}] All configurations valid.",
|
||||
"bg": "[tofu-{mode}] Всички конфигурации са валидни.",
|
||||
"de": "[tofu-{mode}] Alle Konfigurationen gültig.",
|
||||
"pl": "[tofu-{mode}] Wszystkie konfiguracje są poprawne.",
|
||||
"ru": "[tofu-{mode}] Все конфигурации валидны.",
|
||||
"zh": "[tofu-{mode}] 所有配置有效。"
|
||||
},
|
||||
"[tofu-{mode}] Validating OpenTofu configurations...": {
|
||||
"en": "[tofu-{mode}] Validating OpenTofu configurations...",
|
||||
"bg": "[tofu-{mode}] Проверка на OpenTofu конфигурациите...",
|
||||
"de": "[tofu-{mode}] Validiere OpenTofu-Konfigurationen...",
|
||||
"pl": "[tofu-{mode}] Sprawdzanie konfiguracji OpenTofu...",
|
||||
"ru": "[tofu-{mode}] Проверка конфигураций OpenTofu...",
|
||||
"zh": "[tofu-{mode}] 正在验证 OpenTofu 配置..."
|
||||
},
|
||||
"[tool.devx] missing required keys: {keys}": {
|
||||
"bg": "[tool.devx] липсват задължителни ключове: {keys}",
|
||||
"de": "[tool.devx] fehlt erforderliche Schlüssel: {keys}",
|
||||
@@ -2879,6 +3503,14 @@
|
||||
"ru": "tea not installed — skipping login configuration.",
|
||||
"zh": "tea not installed — skipping login configuration."
|
||||
},
|
||||
"tofu command failed in {dir}: {error}": {
|
||||
"en": "tofu command failed in {dir}: {error}",
|
||||
"bg": "командата tofu не успя в {dir}: {error}",
|
||||
"de": "tofu-Befehl fehlgeschlagen in {dir}: {error}",
|
||||
"pl": "polecenie tofu nie powiodło się w {dir}: {error}",
|
||||
"ru": "команда tofu не удалась в {dir}: {error}",
|
||||
"zh": "tofu 命令在 {dir} 中失败: {error}"
|
||||
},
|
||||
"unknown": {
|
||||
"bg": "неизвестен",
|
||||
"de": "unbekannt",
|
||||
@@ -2887,286 +3519,6 @@
|
||||
"ru": "неизвестно",
|
||||
"zh": "未知"
|
||||
},
|
||||
"{file} already exists. Use --force to overwrite.": {
|
||||
"bg": "{file} already exists. Use --force to overwrite.",
|
||||
"de": "{file} already exists. Use --force to overwrite.",
|
||||
"en": "{file} already exists. Use --force to overwrite.",
|
||||
"pl": "{file} już istnieje. Użyj --force, aby nadpisać.",
|
||||
"ru": "{file} already exists. Use --force to overwrite.",
|
||||
"zh": "{file} already exists. Use --force to overwrite."
|
||||
},
|
||||
"{separator}": {
|
||||
"bg": "{separator}",
|
||||
"de": "{separator}",
|
||||
"en": "{separator}",
|
||||
"pl": "{separator}",
|
||||
"ru": "{separator}",
|
||||
"zh": "{separator}"
|
||||
},
|
||||
"Failed to push release commit after 3 attempts. Manual intervention required.": {
|
||||
"bg": "Failed to push release commit after 3 attempts. Manual intervention required.",
|
||||
"de": "Failed to push release commit after 3 attempts. Manual intervention required.",
|
||||
"en": "Failed to push release commit after 3 attempts. Manual intervention required.",
|
||||
"pl": "Failed to push release commit after 3 attempts. Manual intervention required.",
|
||||
"ru": "Failed to push release commit after 3 attempts. Manual intervention required.",
|
||||
"zh": "Failed to push release commit after 3 attempts. Manual intervention required."
|
||||
},
|
||||
"Push attempt {n}/3 failed: {err}": {
|
||||
"bg": "Push attempt {n}/3 failed: {err}",
|
||||
"de": "Push attempt {n}/3 failed: {err}",
|
||||
"en": "Push attempt {n}/3 failed: {err}",
|
||||
"pl": "Push attempt {n}/3 failed: {err}",
|
||||
"ru": "Push attempt {n}/3 failed: {err}",
|
||||
"zh": "Push attempt {n}/3 failed: {err}"
|
||||
},
|
||||
"Rebase attempt {n}/3 failed: {err}": {
|
||||
"bg": "Rebase attempt {n}/3 failed: {err}",
|
||||
"de": "Rebase attempt {n}/3 failed: {err}",
|
||||
"en": "Rebase attempt {n}/3 failed: {err}",
|
||||
"pl": "Rebase attempt {n}/3 failed: {err}",
|
||||
"ru": "Rebase attempt {n}/3 failed: {err}",
|
||||
"zh": "Rebase attempt {n}/3 failed: {err}"
|
||||
},
|
||||
"Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.": {
|
||||
"bg": "Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
|
||||
"de": "Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
|
||||
"en": "Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
|
||||
"pl": "Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
|
||||
"ru": "Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
|
||||
"zh": "Auto-rebase failed with HTTP {status}: {message}\nRebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label."
|
||||
},
|
||||
"Branch is already up-to-date with origin/master.": {
|
||||
"bg": "Branch is already up-to-date with origin/master.",
|
||||
"de": "Branch is already up-to-date with origin/master.",
|
||||
"en": "Branch is already up-to-date with origin/master.",
|
||||
"pl": "Branch is already up-to-date with origin/master.",
|
||||
"ru": "Branch is already up-to-date with origin/master.",
|
||||
"zh": "Branch is already up-to-date with origin/master."
|
||||
},
|
||||
"Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR.": {
|
||||
"bg": "Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR.",
|
||||
"de": "Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR.",
|
||||
"en": "Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR.",
|
||||
"pl": "Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR.",
|
||||
"ru": "Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR.",
|
||||
"zh": "Branch is behind master. Auto-rebasing via Gitea API...\nA new CI run will start automatically after the rebase.\nThe next auto-merge attempt will merge this PR."
|
||||
},
|
||||
"Branch is {count} commit(s) behind master. Rebasing...": {
|
||||
"bg": "Branch is {count} commit(s) behind master. Rebasing...",
|
||||
"de": "Branch is {count} commit(s) behind master. Rebasing...",
|
||||
"en": "Branch is {count} commit(s) behind master. Rebasing...",
|
||||
"pl": "Branch is {count} commit(s) behind master. Rebasing...",
|
||||
"ru": "Branch is {count} commit(s) behind master. Rebasing...",
|
||||
"zh": "Branch is {count} commit(s) behind master. Rebasing..."
|
||||
},
|
||||
"CI_GITEA_TOKEN is not set. Add it to .env or export it.": {
|
||||
"bg": "CI_GITEA_TOKEN is not set. Add it to .env or export it.",
|
||||
"de": "CI_GITEA_TOKEN is not set. Add it to .env or export it.",
|
||||
"en": "CI_GITEA_TOKEN is not set. Add it to .env or export it.",
|
||||
"pl": "CI_GITEA_TOKEN is not set. Add it to .env or export it.",
|
||||
"ru": "CI_GITEA_TOKEN is not set. Add it to .env or export it.",
|
||||
"zh": "CI_GITEA_TOKEN is not set. Add it to .env or export it."
|
||||
},
|
||||
"Cannot rebase: not on a branch (detached HEAD).": {
|
||||
"bg": "Cannot rebase: not on a branch (detached HEAD).",
|
||||
"de": "Cannot rebase: not on a branch (detached HEAD).",
|
||||
"en": "Cannot rebase: not on a branch (detached HEAD).",
|
||||
"pl": "Cannot rebase: not on a branch (detached HEAD).",
|
||||
"ru": "Cannot rebase: not on a branch (detached HEAD).",
|
||||
"zh": "Cannot rebase: not on a branch (detached HEAD)."
|
||||
},
|
||||
"Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR.": {
|
||||
"bg": "Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR.",
|
||||
"de": "Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR.",
|
||||
"en": "Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR.",
|
||||
"pl": "Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR.",
|
||||
"ru": "Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR.",
|
||||
"zh": "Could not detect PR number. Use --pr to specify it explicitly,\nor run this command from a branch with an open PR."
|
||||
},
|
||||
"Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables.": {
|
||||
"bg": "Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables.",
|
||||
"de": "Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables.",
|
||||
"en": "Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables.",
|
||||
"pl": "Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables.",
|
||||
"ru": "Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables.",
|
||||
"zh": "Could not determine repository. Set DEVX_REPO_OWNER and DEVX_REPO_NAME\nor GITHUB_REPOSITORY environment variables."
|
||||
},
|
||||
"Fetch failed: {error}": {
|
||||
"bg": "Fetch failed: {error}",
|
||||
"de": "Fetch failed: {error}",
|
||||
"en": "Fetch failed: {error}",
|
||||
"pl": "Fetch failed: {error}",
|
||||
"ru": "Fetch failed: {error}",
|
||||
"zh": "Fetch failed: {error}"
|
||||
},
|
||||
"Fetching origin/master...": {
|
||||
"bg": "Fetching origin/master...",
|
||||
"de": "Fetching origin/master...",
|
||||
"en": "Fetching origin/master...",
|
||||
"pl": "Fetching origin/master...",
|
||||
"ru": "Fetching origin/master...",
|
||||
"zh": "Fetching origin/master..."
|
||||
},
|
||||
"Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again.": {
|
||||
"bg": "Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again.",
|
||||
"de": "Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again.",
|
||||
"en": "Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again.",
|
||||
"pl": "Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again.",
|
||||
"ru": "Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again.",
|
||||
"zh": "Force-push failed:\n{error}\nThe remote may have unexpected commits. Fetch and try again."
|
||||
},
|
||||
"Force-pushing...": {
|
||||
"bg": "Force-pushing...",
|
||||
"de": "Force-pushing...",
|
||||
"en": "Force-pushing...",
|
||||
"pl": "Force-pushing...",
|
||||
"ru": "Force-pushing...",
|
||||
"zh": "Force-pushing..."
|
||||
},
|
||||
"Nothing to push.": {
|
||||
"bg": "Nothing to push.",
|
||||
"de": "Nothing to push.",
|
||||
"en": "Nothing to push.",
|
||||
"pl": "Nothing to push.",
|
||||
"ru": "Nothing to push.",
|
||||
"zh": "Nothing to push."
|
||||
},
|
||||
"PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR.": {
|
||||
"bg": "PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR.",
|
||||
"de": "PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR.",
|
||||
"en": "PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR.",
|
||||
"pl": "PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR.",
|
||||
"ru": "PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR.",
|
||||
"zh": "PR #{pr} rebased successfully. A new CI run will start automatically.\nIf auto-merge is enabled (ready-to-merge label), the next CI run\nwill attempt to merge this PR."
|
||||
},
|
||||
"Pushed {branch} to origin.": {
|
||||
"bg": "Pushed {branch} to origin.",
|
||||
"de": "Pushed {branch} to origin.",
|
||||
"en": "Pushed {branch} to origin.",
|
||||
"pl": "Pushed {branch} to origin.",
|
||||
"ru": "Pushed {branch} to origin.",
|
||||
"zh": "Pushed {branch} to origin."
|
||||
},
|
||||
"Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue": {
|
||||
"bg": "Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue",
|
||||
"de": "Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue",
|
||||
"en": "Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue",
|
||||
"pl": "Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue",
|
||||
"ru": "Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue",
|
||||
"zh": "Rebase failed (conflicts or other error):\n{error}\nResolve conflicts and run: git rebase --continue"
|
||||
},
|
||||
"Rebase failed with HTTP {status}: {message}": {
|
||||
"bg": "Rebase failed with HTTP {status}: {message}",
|
||||
"de": "Rebase failed with HTTP {status}: {message}",
|
||||
"en": "Rebase failed with HTTP {status}: {message}",
|
||||
"pl": "Rebase failed with HTTP {status}: {message}",
|
||||
"ru": "Rebase failed with HTTP {status}: {message}",
|
||||
"zh": "Rebase failed with HTTP {status}: {message}"
|
||||
},
|
||||
"Rebase successful.": {
|
||||
"bg": "Rebase successful.",
|
||||
"de": "Rebase successful.",
|
||||
"en": "Rebase successful.",
|
||||
"pl": "Rebase successful.",
|
||||
"ru": "Rebase successful.",
|
||||
"zh": "Rebase successful."
|
||||
},
|
||||
"Rebasing PR #{pr} via Gitea API...": {
|
||||
"bg": "Rebasing PR #{pr} via Gitea API...",
|
||||
"de": "Rebasing PR #{pr} via Gitea API...",
|
||||
"en": "Rebasing PR #{pr} via Gitea API...",
|
||||
"pl": "Rebasing PR #{pr} via Gitea API...",
|
||||
"ru": "Rebasing PR #{pr} via Gitea API...",
|
||||
"zh": "Rebasing PR #{pr} via Gitea API..."
|
||||
},
|
||||
"Ensuring standard labels...": {
|
||||
"bg": "Ensuring standard labels...",
|
||||
"de": "Ensuring standard labels...",
|
||||
"en": "Ensuring standard labels...",
|
||||
"pl": "Ensuring standard labels...",
|
||||
"ru": "Ensuring standard labels...",
|
||||
"zh": "Ensuring standard labels..."
|
||||
},
|
||||
" - {count} standard labels verified": {
|
||||
"bg": " - {count} standard labels verified",
|
||||
"de": " - {count} standard labels verified",
|
||||
"en": " - {count} standard labels verified",
|
||||
"pl": " - {count} standard labels verified",
|
||||
"ru": " - {count} standard labels verified",
|
||||
"zh": " - {count} standard labels verified"
|
||||
},
|
||||
"[check-deps] Virtualenv .venv ready (Python {version}).": {
|
||||
"en": "[check-deps] Virtualenv .venv ready (Python {version}).",
|
||||
"bg": "[check-deps] Виртуална среда .venv готова (Python {version}).",
|
||||
"de": "[check-deps] Virtuelle Umgebung .venv bereit (Python {version}).",
|
||||
"pl": "[check-deps] Środowisko wirtualne .venv gotowe (Python {version}).",
|
||||
"ru": "[check-deps] Виртуальное окружение .venv готово (Python {version}).",
|
||||
"zh": "[check-deps] 虚拟环境 .venv 已就绪 (Python {version})。"
|
||||
},
|
||||
"{level}: {tool} not found.{hint}": {
|
||||
"en": "{level}: {tool} not found.{hint}",
|
||||
"bg": "{level}: {tool} не е намерен.{hint}",
|
||||
"de": "{level}: {tool} nicht gefunden.{hint}",
|
||||
"pl": "{level}: {tool} nie znaleziono.{hint}",
|
||||
"ru": "{level}: {tool} не найден.{hint}",
|
||||
"zh": "{level}: 未找到 {tool}。{hint}"
|
||||
},
|
||||
"WARN: Could not determine Python version in .venv.": {
|
||||
"en": "WARN: Could not determine Python version in .venv.",
|
||||
"bg": "ПРЕДУПРЕЖДЕНИЕ: Не може да се определи версията на Python в .venv.",
|
||||
"de": "WARNUNG: Python-Version in .venv konnte nicht bestimmt werden.",
|
||||
"pl": "OSTRZEŻENIE: Nie można określić wersji Python w .venv.",
|
||||
"ru": "ПРЕДУПРЕЖДЕНИЕ: Не удалось определить версию Python в .venv.",
|
||||
"zh": "警告: 无法确定 .venv 中的 Python 版本。"
|
||||
},
|
||||
"WARN: Could not parse Python version '{version}'.": {
|
||||
"en": "WARN: Could not parse Python version '{version}'.",
|
||||
"bg": "ПРЕДУПРЕЖДЕНИЕ: Не може да се анализира версията на Python '{version}'.",
|
||||
"de": "WARNUNG: Python-Version '{version}' konnte nicht analysiert werden.",
|
||||
"pl": "OSTRZEŻENIE: Nie można przeanalizować wersji Python '{version}'.",
|
||||
"ru": "ПРЕДУПРЕЖДЕНИЕ: Не удалось разобрать версию Python '{version}'.",
|
||||
"zh": "警告: 无法解析 Python 版本 '{version}'。"
|
||||
},
|
||||
"WARN: .venv not found. Run 'make setup-venv' to create it.": {
|
||||
"en": "WARN: .venv not found. Run 'make setup-venv' to create it.",
|
||||
"bg": "ПРЕДУПРЕЖДЕНИЕ: .venv не е намерен. Изпълнете 'make setup-venv' за създаване.",
|
||||
"de": "WARNUNG: .venv nicht gefunden. Führen Sie 'make setup-venv' aus, um es zu erstellen.",
|
||||
"pl": "OSTRZEŻENIE: Nie znaleziono .venv. Uruchom 'make setup-venv', aby utworzyć.",
|
||||
"ru": "ПРЕДУПРЕЖДЕНИЕ: .venv не найден. Выполните 'make setup-venv' для создания.",
|
||||
"zh": "警告: 未找到 .venv。运行 'make setup-venv' 来创建。"
|
||||
},
|
||||
"[docker-login] Logged in to {registry}.": {
|
||||
"en": "[docker-login] Logged in to {registry}.",
|
||||
"bg": "[docker-login] Влязъл в {registry}.",
|
||||
"de": "[docker-login] Angemeldet bei {registry}.",
|
||||
"pl": "[docker-login] Zalogowano do {registry}.",
|
||||
"ru": "[docker-login] Выполнен вход в {registry}.",
|
||||
"zh": "[docker-login] 已登录到 {registry}。"
|
||||
},
|
||||
"[docker-login] Login to {registry} failed (continuing).": {
|
||||
"en": "[docker-login] Login to {registry} failed (continuing).",
|
||||
"bg": "[docker-login] Влизането в {registry} не успя (продължава).",
|
||||
"de": "[docker-login] Anmeldung bei {registry} fehlgeschlagen (wird fortgesetzt).",
|
||||
"pl": "[docker-login] Logowanie do {registry} nie powiodło się (kontynuowanie).",
|
||||
"ru": "[docker-login] Ошибка входа в {registry} (продолжаем).",
|
||||
"zh": "[docker-login] 登录 {registry} 失败(继续)。"
|
||||
},
|
||||
"[docker-login] Skipping {registry} (token {env} not set).": {
|
||||
"en": "[docker-login] Skipping {registry} (token {env} not set).",
|
||||
"bg": "[docker-login] Пропускане на {registry} (токен {env} не е зададен).",
|
||||
"de": "[docker-login] {registry} übersprungen (Token {env} nicht gesetzt).",
|
||||
"pl": "[docker-login] Pomijanie {registry} (token {env} nie ustawiony).",
|
||||
"ru": "[docker-login] Пропуск {registry} (токен {env} не задан).",
|
||||
"zh": "[docker-login] 跳过 {registry}(未设置令牌 {env})。"
|
||||
},
|
||||
"{env} is not set. Set it in your .env file.": {
|
||||
"en": "{env} is not set. Set it in your .env file.",
|
||||
"bg": "{env} не е зададен. Задайте го във вашия .env файл.",
|
||||
"de": "{env} ist nicht gesetzt. Setzen Sie es in Ihrer .env-Datei.",
|
||||
"pl": "{env} nie jest ustawiony. Ustaw go w pliku .env.",
|
||||
"ru": "{env} не задан. Установите его в файле .env.",
|
||||
"zh": "{env} 未设置。请在 .env 文件中设置。"
|
||||
},
|
||||
"{env} is not set. Set it in your .env file or pass it as an environment variable.": {
|
||||
"en": "{env} is not set. Set it in your .env file or pass it as an environment variable.",
|
||||
"bg": "{env} не е зададен. Задайте го във вашия .env файл или го подайте като променлива на средата.",
|
||||
@@ -3175,188 +3527,36 @@
|
||||
"ru": "{env} не задан. Установите его в файле .env или передайте как переменную окружения.",
|
||||
"zh": "{env} 未设置。请在 .env 文件中设置或作为环境变量传递。"
|
||||
},
|
||||
"Login to {registry} failed: {error}": {
|
||||
"en": "Login to {registry} failed: {error}",
|
||||
"bg": "Влизането в {registry} не успя: {error}",
|
||||
"de": "Anmeldung bei {registry} fehlgeschlagen: {error}",
|
||||
"pl": "Logowanie do {registry} nie powiodło się: {error}",
|
||||
"ru": "Ошибка входа в {registry}: {error}",
|
||||
"zh": "登录 {registry} 失败: {error}"
|
||||
"{env} is not set. Set it in your .env file.": {
|
||||
"en": "{env} is not set. Set it in your .env file.",
|
||||
"bg": "{env} не е зададен. Задайте го във вашия .env файл.",
|
||||
"de": "{env} ist nicht gesetzt. Setzen Sie es in Ihrer .env-Datei.",
|
||||
"pl": "{env} nie jest ustawiony. Ustaw go w pliku .env.",
|
||||
"ru": "{env} не задан. Установите его в файле .env.",
|
||||
"zh": "{env} 未设置。请在 .env 文件中设置。"
|
||||
},
|
||||
"tofu command failed in {dir}: {error}": {
|
||||
"en": "tofu command failed in {dir}: {error}",
|
||||
"bg": "командата tofu не успя в {dir}: {error}",
|
||||
"de": "tofu-Befehl fehlgeschlagen in {dir}: {error}",
|
||||
"pl": "polecenie tofu nie powiodło się w {dir}: {error}",
|
||||
"ru": "команда tofu не удалась в {dir}: {error}",
|
||||
"zh": "tofu 命令在 {dir} 中失败: {error}"
|
||||
"{file} already exists. Use --force to overwrite.": {
|
||||
"bg": "{file} already exists. Use --force to overwrite.",
|
||||
"de": "{file} already exists. Use --force to overwrite.",
|
||||
"en": "{file} already exists. Use --force to overwrite.",
|
||||
"pl": "{file} już istnieje. Użyj --force, aby nadpisać.",
|
||||
"ru": "{file} already exists. Use --force to overwrite.",
|
||||
"zh": "{file} already exists. Use --force to overwrite."
|
||||
},
|
||||
"WARN: .venv has Python {version}, but >={req} is required.": {
|
||||
"en": "WARN: .venv has Python {version}, but >={req} is required.",
|
||||
"bg": "ПРЕДУПРЕЖДЕНИЕ: .venv има Python {version}, но се изисква >={req}.",
|
||||
"de": "WARNUNG: .venv hat Python {version}, aber >={req} ist erforderlich.",
|
||||
"pl": "OSTRZEŻENIE: .venv ma Python {version}, ale wymagane jest >={req}.",
|
||||
"ru": "ПРЕДУПРЕЖДЕНИЕ: в .venv установлен Python {version}, но требуется >={req}.",
|
||||
"zh": "警告: .venv 的 Python 版本为 {version},但要求 >={req}。"
|
||||
"{level}: {tool} not found.{hint}": {
|
||||
"en": "{level}: {tool} not found.{hint}",
|
||||
"bg": "{level}: {tool} не е намерен.{hint}",
|
||||
"de": "{level}: {tool} nicht gefunden.{hint}",
|
||||
"pl": "{level}: {tool} nie znaleziono.{hint}",
|
||||
"ru": "{level}: {tool} не найден.{hint}",
|
||||
"zh": "{level}: 未找到 {tool}。{hint}"
|
||||
},
|
||||
" -> {dir}": {
|
||||
"en": " -> {dir}",
|
||||
"bg": " -> {dir}",
|
||||
"de": " -> {dir}",
|
||||
"pl": " -> {dir}",
|
||||
"ru": " -> {dir}",
|
||||
"zh": " -> {dir}"
|
||||
},
|
||||
"[tofu-init] Initializing {dir}...": {
|
||||
"en": "[tofu-init] Initializing {dir}...",
|
||||
"bg": "[tofu-init] Инициализиране на {dir}...",
|
||||
"de": "[tofu-init] Initialisiere {dir}...",
|
||||
"pl": "[tofu-init] Inicjalizacja {dir}...",
|
||||
"ru": "[tofu-init] Инициализация {dir}...",
|
||||
"zh": "[tofu-init] 正在初始化 {dir}..."
|
||||
},
|
||||
"[tofu-init] Done.": {
|
||||
"en": "[tofu-init] Done.",
|
||||
"bg": "[tofu-init] Готово.",
|
||||
"de": "[tofu-init] Fertig.",
|
||||
"pl": "[tofu-init] Gotowe.",
|
||||
"ru": "[tofu-init] Готово.",
|
||||
"zh": "[tofu-init] 完成。"
|
||||
},
|
||||
"[tofu-{mode}] Validating OpenTofu configurations...": {
|
||||
"en": "[tofu-{mode}] Validating OpenTofu configurations...",
|
||||
"bg": "[tofu-{mode}] Проверка на OpenTofu конфигурациите...",
|
||||
"de": "[tofu-{mode}] Validiere OpenTofu-Konfigurationen...",
|
||||
"pl": "[tofu-{mode}] Sprawdzanie konfiguracji OpenTofu...",
|
||||
"ru": "[tofu-{mode}] Проверка конфигураций OpenTofu...",
|
||||
"zh": "[tofu-{mode}] 正在验证 OpenTofu 配置..."
|
||||
},
|
||||
"[tofu-{mode}] All configurations valid.": {
|
||||
"en": "[tofu-{mode}] All configurations valid.",
|
||||
"bg": "[tofu-{mode}] Всички конфигурации са валидни.",
|
||||
"de": "[tofu-{mode}] Alle Konfigurationen gültig.",
|
||||
"pl": "[tofu-{mode}] Wszystkie konfiguracje są poprawne.",
|
||||
"ru": "[tofu-{mode}] Все конфигурации валидны.",
|
||||
"zh": "[tofu-{mode}] 所有配置有效。"
|
||||
},
|
||||
"[check-deps] Verifying tools...": {
|
||||
"en": "[check-deps] Verifying tools...",
|
||||
"bg": "[check-deps] Проверка на инструментите...",
|
||||
"de": "[check-deps] Werkzeuge werden überprüft...",
|
||||
"pl": "[check-deps] Sprawdzanie narzędzi...",
|
||||
"ru": "[check-deps] Проверка инструментов...",
|
||||
"zh": "[check-deps] 正在验证工具..."
|
||||
},
|
||||
" {tool}: found at {path}": {
|
||||
"en": " {tool}: found at {path}",
|
||||
"bg": " {tool}: намерен на {path}",
|
||||
"de": " {tool}: gefunden unter {path}",
|
||||
"pl": " {tool}: znaleziono w {path}",
|
||||
"ru": " {tool}: найден в {path}",
|
||||
"zh": " {tool}: 在 {path} 找到"
|
||||
},
|
||||
" Run 'make install-checkmake' to install the Makefile linter.": {
|
||||
"en": " Run 'make install-checkmake' to install the Makefile linter.",
|
||||
"bg": " Изпълнете 'make install-checkmake' за инсталиране на Makefile линтера.",
|
||||
"de": " Führen Sie 'make install-checkmake' aus, um den Makefile-Linter zu installieren.",
|
||||
"pl": " Uruchom 'make install-checkmake', aby zainstalować linter Makefile.",
|
||||
"ru": " Выполните 'make install-checkmake' для установки линтера Makefile.",
|
||||
"zh": " 运行 'make install-checkmake' 来安装 Makefile 检查器。"
|
||||
},
|
||||
"Required tools missing.": {
|
||||
"en": "Required tools missing.",
|
||||
"bg": "Липсват задължителни инструменти.",
|
||||
"de": "Erforderliche Werkzeuge fehlen.",
|
||||
"pl": "Brak wymaganych narzędzi.",
|
||||
"ru": "Отсутствуют обязательные инструменты.",
|
||||
"zh": "缺少必需的工具。"
|
||||
},
|
||||
"[check-deps] All core tools present.": {
|
||||
"en": "[check-deps] All core tools present.",
|
||||
"bg": "[check-deps] Всички основни инструменти са налични.",
|
||||
"de": "[check-deps] Alle Kernwerkzeuge vorhanden.",
|
||||
"pl": "[check-deps] Wszystkie podstawowe narzędzia są dostępne.",
|
||||
"ru": "[check-deps] Все основные инструменты доступны.",
|
||||
"zh": "[check-deps] 所有核心工具均已就绪。"
|
||||
},
|
||||
"SSH_PRIVATE_KEY not set — skipping SSH key setup": {
|
||||
"en": "SSH_PRIVATE_KEY not set — skipping SSH key setup",
|
||||
"bg": "SSH_PRIVATE_KEY не е зададен — пропускане на SSH ключ настройката",
|
||||
"de": "SSH_PRIVATE_KEY nicht gesetzt — SSH-Schlüssel-Setup übersprungen",
|
||||
"pl": "SSH_PRIVATE_KEY nie ustawione — pomijanie konfiguracji klucza SSH",
|
||||
"ru": "SSH_PRIVATE_KEY не задан — пропуск настройки SSH-ключа",
|
||||
"zh": "SSH_PRIVATE_KEY 未设置 — 跳过 SSH 密钥设置"
|
||||
},
|
||||
"Failed to start ssh-agent: {error}": {
|
||||
"en": "Failed to start ssh-agent: {error}",
|
||||
"bg": "Неуспешно стартиране на ssh-agent: {error}",
|
||||
"de": "Starten von ssh-agent fehlgeschlagen: {error}",
|
||||
"pl": "Nie udało się uruchomić ssh-agent: {error}",
|
||||
"ru": "Не удалось запустить ssh-agent: {error}",
|
||||
"zh": "启动 ssh-agent 失败: {error}"
|
||||
},
|
||||
"SSH key set up successfully": {
|
||||
"en": "SSH key set up successfully",
|
||||
"bg": "SSH ключът е настроен успешно",
|
||||
"de": "SSH-Schlüssel erfolgreich eingerichtet",
|
||||
"pl": "Klucz SSH skonfigurowany pomyślnie",
|
||||
"ru": "SSH-ключ успешно настроен",
|
||||
"zh": "SSH 密钥设置成功"
|
||||
},
|
||||
"SSH key setup skipped (no key provided)": {
|
||||
"en": "SSH key setup skipped (no key provided)",
|
||||
"bg": "Настройката на SSH ключ е пропусната (не е предоставен ключ)",
|
||||
"de": "SSH-Schlüssel-Setup übersprungen (kein Schlüssel bereitgestellt)",
|
||||
"pl": "Pominięto konfigurację klucza SSH (brak klucza)",
|
||||
"ru": "Настройка SSH-ключа пропущена (ключ не предоставлен)",
|
||||
"zh": "SSH 密钥设置已跳过(未提供密钥)"
|
||||
},
|
||||
"Found {count} unsafe identity check(s) in integration tests.": {
|
||||
"en": "Found {count} unsafe identity check(s) in integration tests.",
|
||||
"bg": "Намерени са {count} небрежни проверки за идентичност в интеграционните тестове.",
|
||||
"de": "{count} unsichere Identitätsprüfung(en) in Integrationstests gefunden.",
|
||||
"pl": "Znaleziono {count} niebezpiecznych sprawdzeń tożsamości w testach integracyjnych.",
|
||||
"ru": "Найдено {count} небезопасных проверок идентичности в интеграционных тестах.",
|
||||
"zh": "在集成测试中发现 {count} 个不安全的身份检查。"
|
||||
},
|
||||
"Use string comparison or _is_truthy()/_is_falsy() helpers instead. Add '{marker}' to suppress individual lines.": {
|
||||
"en": "Use string comparison or _is_truthy()/_is_falsy() helpers instead. Add '{marker}' to suppress individual lines.",
|
||||
"bg": "Използвайте сравнение на низове или _is_truthy()/_is_falsy() помощници. Добавете '{marker}' за потискане на отделни редове.",
|
||||
"de": "Verwenden Sie String-Vergleich oder _is_truthy()/_is_falsy() Hilfsfunktionen. Fügen Sie '{marker}' hinzu, um einzelne Zeilen zu unterdrücken.",
|
||||
"pl": "Użyj porównania ciągów lub pomocników _is_truthy()/_is_falsy(). Dodaj '{marker}', aby pominąć pojedyncze linie.",
|
||||
"ru": "Используйте строковое сравнение или помощники _is_truthy()/_is_falsy(). Добавьте '{marker}' для подавления отдельных строк.",
|
||||
"zh": "使用字符串比较或 _is_truthy()/_is_falsy() 辅助函数。添加 '{marker}' 以抑制个别行。"
|
||||
},
|
||||
"[check-api-identity-checks] Passed: no unsafe identity checks found": {
|
||||
"en": "[check-api-identity-checks] Passed: no unsafe identity checks found",
|
||||
"bg": "[check-api-identity-checks] Мина: не са намерени небрежни проверки за идентичност",
|
||||
"de": "[check-api-identity-checks] Bestanden: keine unsicheren Identitätsprüfungen gefunden",
|
||||
"pl": "[check-api-identity-checks] Passed: nie znaleziono niebezpiecznych sprawdzeń tożsamości",
|
||||
"ru": "[check-api-identity-checks] Пройдено: небезопасных проверок идентичности не найдено",
|
||||
"zh": "[check-api-identity-checks] 通过:未发现不安全的身份检查"
|
||||
},
|
||||
"Directory to scan (default: tests/integration). Can be repeated.": {
|
||||
"en": "Directory to scan (default: tests/integration). Can be repeated.",
|
||||
"bg": "Директория за сканиране (по подразбиране: tests/integration). Може да се повтаря.",
|
||||
"de": "Zu scannendes Verzeichnis (Standard: tests/integration). Kann wiederholt werden.",
|
||||
"pl": "Katalog do skanowania (domyślnie: tests/integration). Można powtarzać.",
|
||||
"ru": "Директория для сканирования (по умолчанию: tests/integration). Можно повторять.",
|
||||
"zh": "要扫描的目录(默认:tests/integration)。可重复。"
|
||||
},
|
||||
"Failed to list existing wiki pages after retries: {error}. Aborting to avoid creating duplicate pages.": {
|
||||
"bg": "Неуспешно извличане на съществуващи wiki страници след повторни опити: {error}. Прекратяване, за да се избегне създаване на дублирани страници.",
|
||||
"de": "Abrufen bestehender Wiki-Seiten nach Wiederholungen fehlgeschlagen: {error}. Abbruch, um doppelte Seiten zu vermeiden.",
|
||||
"en": "Failed to list existing wiki pages after retries: {error}. Aborting to avoid creating duplicate pages.",
|
||||
"pl": "Nie udało się wylistować istniejących stron wiki po ponownych próbach: {error}. Przerywanie, aby uniknąć tworzenia zduplikowanych stron.",
|
||||
"ru": "Не удалось получить список существующих wiki-страниц после повторных попыток: {error}. Прерывание, чтобы избежать создания дубликатов страниц.",
|
||||
"zh": "重试后列出现有 wiki 页面失败:{error}。正在中止以避免创建重复页面。"
|
||||
},
|
||||
" Page '{title}' already exists (stale list). Re-listing and updating...": {
|
||||
"bg": " Страницата '{title}' вече съществува (остарял списък). Пресписване и обновяване...",
|
||||
"de": " Seite '{title}' existiert bereits (veraltete Liste). Neu auflisten und aktualisieren...",
|
||||
"en": " Page '{title}' already exists (stale list). Re-listing and updating...",
|
||||
"pl": " Strona '{title}' już istnieje (nieaktualna lista). Ponowne listowanie i aktualizacja...",
|
||||
"ru": " Страница '{title}' уже существует (устаревший список). Повторное получение списка и обновление...",
|
||||
"zh": " 页面 '{title}' 已存在(列表过期)。重新列出并更新..."
|
||||
"{separator}": {
|
||||
"bg": "{separator}",
|
||||
"de": "{separator}",
|
||||
"en": "{separator}",
|
||||
"pl": "{separator}",
|
||||
"ru": "{separator}",
|
||||
"zh": "{separator}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -909,3 +909,65 @@ class TestGiteaClientActions:
|
||||
"https://git.example.com/repos/owner/repo/actions/jobs/10026/logs",
|
||||
timeout=DEFAULT_TIMEOUT,
|
||||
)
|
||||
|
||||
def test_get_repo_variable_returns_value(self) -> None:
|
||||
client = GiteaClient("https://git.example.com", "tok", "owner", "repo")
|
||||
client._session.request = MagicMock(return_value=_mock_response({"value": "v0.28.1"}))
|
||||
result = client.get_repo_variable("PRODUCTION_DEPLOY_TAG")
|
||||
assert result == "v0.28.1"
|
||||
client._session.request.assert_called_once_with(
|
||||
"GET",
|
||||
"https://git.example.com/repos/owner/repo/actions/variables/PRODUCTION_DEPLOY_TAG",
|
||||
timeout=DEFAULT_TIMEOUT,
|
||||
)
|
||||
|
||||
def test_get_repo_variable_returns_none_on_404(self) -> None:
|
||||
client = GiteaClient("https://git.example.com", "tok", "owner", "repo")
|
||||
not_found = MagicMock()
|
||||
not_found.raise_for_status.side_effect = _mock_http_error(404, "not found")
|
||||
client._session.request = MagicMock(return_value=not_found)
|
||||
result = client.get_repo_variable("MISSING_VAR")
|
||||
assert result is None
|
||||
|
||||
@patch("time.sleep")
|
||||
def test_get_repo_variable_reraises_non_404(self, mock_sleep: MagicMock) -> None:
|
||||
client = GiteaClient("https://git.example.com", "tok", "owner", "repo")
|
||||
server_error = MagicMock()
|
||||
server_error.raise_for_status.side_effect = _mock_http_error(500, "server error")
|
||||
client._session.request = MagicMock(return_value=server_error)
|
||||
with pytest.raises(APIError) as exc_info:
|
||||
client.get_repo_variable("SOME_VAR")
|
||||
assert exc_info.value.status == 500
|
||||
|
||||
def test_set_repo_variable_updates_existing(self) -> None:
|
||||
client = GiteaClient("https://git.example.com", "tok", "owner", "repo")
|
||||
client._session.request = MagicMock(return_value=_mock_response({}))
|
||||
client.set_repo_variable("PRODUCTION_DEPLOY_TAG", "v0.28.2")
|
||||
client._session.request.assert_called_once_with(
|
||||
"PUT",
|
||||
"https://git.example.com/repos/owner/repo/actions/variables/PRODUCTION_DEPLOY_TAG",
|
||||
timeout=DEFAULT_TIMEOUT,
|
||||
json={"value": "v0.28.2"},
|
||||
)
|
||||
|
||||
def test_set_repo_variable_creates_on_404(self) -> None:
|
||||
client = GiteaClient("https://git.example.com", "tok", "owner", "repo")
|
||||
not_found = MagicMock()
|
||||
not_found.raise_for_status.side_effect = _mock_http_error(404, "not found")
|
||||
created = _mock_response({})
|
||||
client._session.request = MagicMock(side_effect=[not_found, created])
|
||||
client.set_repo_variable("NEW_VAR", "v0.29.0")
|
||||
assert client._session.request.call_count == 2
|
||||
second_call = client._session.request.call_args_list[1]
|
||||
assert second_call.args[0] == "POST"
|
||||
assert second_call.args[1] == "https://git.example.com/repos/owner/repo/actions/variables/NEW_VAR"
|
||||
assert second_call.kwargs["json"] == {"value": "v0.29.0"}
|
||||
|
||||
def test_set_repo_variable_reraises_non_404(self) -> None:
|
||||
client = GiteaClient("https://git.example.com", "tok", "owner", "repo")
|
||||
forbidden = MagicMock()
|
||||
forbidden.raise_for_status.side_effect = _mock_http_error(403, "forbidden")
|
||||
client._session.request = MagicMock(return_value=forbidden)
|
||||
with pytest.raises(APIError) as exc_info:
|
||||
client.set_repo_variable("SOME_VAR", "val")
|
||||
assert exc_info.value.status == 403
|
||||
|
||||
@@ -55,6 +55,38 @@ class TestExtractCliCommands:
|
||||
assert "real_cmd" in commands
|
||||
assert "pass" not in commands
|
||||
|
||||
def test_command_with_explicit_name_param(self, tmp_path: Path) -> None:
|
||||
"""When a command uses name="explicit-name", that name is extracted."""
|
||||
fake_cli = tmp_path / "cli.py"
|
||||
fake_cli.write_text(
|
||||
'@click.group()\ndef cli():\n pass\n@cli.command(name="my-command")\ndef my_command():\n pass\n'
|
||||
)
|
||||
commands = extract_cli_commands(tmp_path)
|
||||
assert "my-command" in commands
|
||||
assert "my_command" not in commands
|
||||
|
||||
def test_command_with_help_kwarg_uses_def_name(self, tmp_path: Path) -> None:
|
||||
"""When a command uses help= kwarg but no name=, falls back to def name."""
|
||||
fake_cli = tmp_path / "cli.py"
|
||||
fake_cli.write_text(
|
||||
"@click.group()\ndef cli():\n pass\n"
|
||||
'@cli.command(help="Do something useful")\ndef do_something():\n pass\n'
|
||||
)
|
||||
commands = extract_cli_commands(tmp_path)
|
||||
assert "do_something" in commands
|
||||
assert "Do something useful" not in commands
|
||||
|
||||
def test_command_with_help_translation_uses_def_name(self, tmp_path: Path) -> None:
|
||||
"""When a command uses help=_() translation, falls back to def name."""
|
||||
fake_cli = tmp_path / "cli.py"
|
||||
fake_cli.write_text(
|
||||
"@click.group()\ndef cli():\n pass\n"
|
||||
'@cli.command(help=_("Install and configure things"))\ndef install():\n pass\n'
|
||||
)
|
||||
commands = extract_cli_commands(tmp_path)
|
||||
assert "install" in commands
|
||||
assert "Install and configure things" not in commands
|
||||
|
||||
|
||||
class TestCheckCommandDocumented:
|
||||
def test_finds_command_in_heading(self) -> None:
|
||||
@@ -195,3 +227,171 @@ class TestMain:
|
||||
result = runner.invoke(main, ["--docs-dir", str(docs)])
|
||||
# No source dir found, so no CLI commands, but modules/scripts from REQUIRED lists
|
||||
assert result.exit_code == 0
|
||||
|
||||
def test_ci_scripts_dir_empty_skips_ci_checks(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""When --ci-scripts-dir is empty string, CI script checks are skipped."""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
docs = tmp_path / "docs"
|
||||
(docs / "user").mkdir(parents=True)
|
||||
(docs / "tech").mkdir(parents=True)
|
||||
src = tmp_path / "src" / "devx"
|
||||
src.mkdir(parents=True)
|
||||
(src / "__init__.py").write_text("")
|
||||
(src / "cli.py").write_text(
|
||||
"@click.group()\ndef cli():\n pass\n@cli.command('release')\ndef release():\n pass\n"
|
||||
)
|
||||
(src / "config.py").write_text("# config")
|
||||
(docs / "user" / "cli-commands.md").write_text("## release\n")
|
||||
(docs / "tech" / "architecture.md").write_text("config.py")
|
||||
(docs / "tech" / "ci-cd-workflow.md").write_text("")
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["--docs-dir", str(docs), "--source-dir", str(src), "--ci-scripts-dir", ""])
|
||||
assert result.exit_code == 0
|
||||
assert "100%" in result.output
|
||||
# Should not mention any CI scripts
|
||||
assert "MISSING" not in result.output or "CI script" not in result.output
|
||||
|
||||
def test_ci_scripts_dir_explicit_path(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""When --ci-scripts-dir points to a directory, scripts are detected from there."""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
docs = tmp_path / "docs"
|
||||
(docs / "user").mkdir(parents=True)
|
||||
(docs / "tech").mkdir(parents=True)
|
||||
src = tmp_path / "src" / "myapp"
|
||||
src.mkdir(parents=True)
|
||||
(src / "__init__.py").write_text("")
|
||||
(src / "cli.py").write_text(
|
||||
"@click.group()\ndef cli():\n pass\n@cli.command('release')\ndef release():\n pass\n"
|
||||
)
|
||||
ci_dir = tmp_path / "ci"
|
||||
ci_dir.mkdir()
|
||||
(ci_dir / "my_script.py").write_text("# my script")
|
||||
(ci_dir / "__init__.py").write_text("")
|
||||
(docs / "user" / "cli-commands.md").write_text("## release\n")
|
||||
(docs / "tech" / "architecture.md").write_text("")
|
||||
(docs / "tech" / "ci-cd-workflow.md").write_text("my_script.py")
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
main, ["--docs-dir", str(docs), "--source-dir", str(src), "--ci-scripts-dir", str(ci_dir)]
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "my_script.py" in result.output
|
||||
assert "OK: my_script.py" in result.output
|
||||
|
||||
def test_ci_scripts_dir_nonexistent_skips(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""When --ci-scripts-dir points to a non-existent path, CI checks are skipped."""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
docs = tmp_path / "docs"
|
||||
(docs / "user").mkdir(parents=True)
|
||||
(docs / "tech").mkdir(parents=True)
|
||||
src = tmp_path / "src" / "myapp"
|
||||
src.mkdir(parents=True)
|
||||
(src / "__init__.py").write_text("")
|
||||
(src / "cli.py").write_text(
|
||||
"@click.group()\ndef cli():\n pass\n@cli.command('release')\ndef release():\n pass\n"
|
||||
)
|
||||
(docs / "user" / "cli-commands.md").write_text("## release\n")
|
||||
(docs / "tech" / "architecture.md").write_text("")
|
||||
(docs / "tech" / "ci-cd-workflow.md").write_text("")
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
main, ["--docs-dir", str(docs), "--source-dir", str(src), "--ci-scripts-dir", "/nonexistent"]
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "100%" in result.output
|
||||
|
||||
def test_config_from_pyproject_ci_scripts_dir(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""When [tool.devx.doc_coverage] ci_scripts_dir is set in pyproject.toml, it's used."""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
docs = tmp_path / "docs"
|
||||
(docs / "user").mkdir(parents=True)
|
||||
(docs / "tech").mkdir(parents=True)
|
||||
src = tmp_path / "src" / "myapp"
|
||||
src.mkdir(parents=True)
|
||||
(src / "__init__.py").write_text("")
|
||||
(src / "cli.py").write_text(
|
||||
"@click.group()\ndef cli():\n pass\n@cli.command('release')\ndef release():\n pass\n"
|
||||
)
|
||||
(src / "config.py").write_text("# config")
|
||||
(docs / "user" / "cli-commands.md").write_text("## release\n")
|
||||
(docs / "tech" / "architecture.md").write_text("config.py")
|
||||
(docs / "tech" / "ci-cd-workflow.md").write_text("")
|
||||
# Write pyproject.toml with ci_scripts_dir = ""
|
||||
(tmp_path / "pyproject.toml").write_text('[tool.devx.doc_coverage]\nci_scripts_dir = ""\n')
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["--docs-dir", str(docs), "--source-dir", str(src)])
|
||||
assert result.exit_code == 0
|
||||
assert "100%" in result.output
|
||||
|
||||
def test_config_from_pyproject_docs_dir(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""When [tool.devx.doc_coverage] docs_dir is set in pyproject.toml, it's used."""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
custom_docs = tmp_path / "custom-docs"
|
||||
(custom_docs / "user").mkdir(parents=True)
|
||||
(custom_docs / "tech").mkdir(parents=True)
|
||||
src = tmp_path / "src" / "myapp"
|
||||
src.mkdir(parents=True)
|
||||
(src / "__init__.py").write_text("")
|
||||
(src / "cli.py").write_text(
|
||||
"@click.group()\ndef cli():\n pass\n@cli.command('release')\ndef release():\n pass\n"
|
||||
)
|
||||
(src / "config.py").write_text("# config")
|
||||
(custom_docs / "user" / "cli-commands.md").write_text("## release\n")
|
||||
(custom_docs / "tech" / "architecture.md").write_text("config.py")
|
||||
(custom_docs / "tech" / "ci-cd-workflow.md").write_text("")
|
||||
# Write pyproject.toml with custom docs_dir
|
||||
(tmp_path / "pyproject.toml").write_text(
|
||||
f'[tool.devx.doc_coverage]\ndocs_dir = "{custom_docs}"\nci_scripts_dir = ""\n'
|
||||
)
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["--source-dir", str(src)])
|
||||
assert result.exit_code == 0
|
||||
assert "100%" in result.output
|
||||
|
||||
def test_config_from_pyproject_source_dir(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""When [tool.devx.doc_coverage] source_dir is set in pyproject.toml, it's used."""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
docs = tmp_path / "docs"
|
||||
(docs / "user").mkdir(parents=True)
|
||||
(docs / "tech").mkdir(parents=True)
|
||||
custom_src = tmp_path / "custom-src" / "myapp"
|
||||
custom_src.mkdir(parents=True)
|
||||
(custom_src / "__init__.py").write_text("")
|
||||
(custom_src / "cli.py").write_text(
|
||||
"@click.group()\ndef cli():\n pass\n@cli.command('release')\ndef release():\n pass\n"
|
||||
)
|
||||
(custom_src / "config.py").write_text("# config")
|
||||
(docs / "user" / "cli-commands.md").write_text("## release\n")
|
||||
(docs / "tech" / "architecture.md").write_text("config.py")
|
||||
(docs / "tech" / "ci-cd-workflow.md").write_text("")
|
||||
# Write pyproject.toml with custom source_dir
|
||||
(tmp_path / "pyproject.toml").write_text(
|
||||
f'[tool.devx.doc_coverage]\nsource_dir = "{custom_src}"\nci_scripts_dir = ""\n'
|
||||
)
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["--docs-dir", str(docs)])
|
||||
assert result.exit_code == 0
|
||||
assert "100%" in result.output
|
||||
|
||||
def test_config_doc_coverage_not_dict(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""When [tool.devx.doc_coverage] is not a dict, falls back to defaults."""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
docs = tmp_path / "docs"
|
||||
(docs / "user").mkdir(parents=True)
|
||||
(docs / "tech").mkdir(parents=True)
|
||||
src = tmp_path / "src" / "myapp"
|
||||
src.mkdir(parents=True)
|
||||
(src / "__init__.py").write_text("")
|
||||
(src / "cli.py").write_text(
|
||||
"@click.group()\ndef cli():\n pass\n@cli.command('release')\ndef release():\n pass\n"
|
||||
)
|
||||
(src / "config.py").write_text("# config")
|
||||
(docs / "user" / "cli-commands.md").write_text("## release\n")
|
||||
(docs / "tech" / "architecture.md").write_text("config.py")
|
||||
(docs / "tech" / "ci-cd-workflow.md").write_text("")
|
||||
# Write pyproject.toml with doc_coverage as a non-dict value
|
||||
(tmp_path / "pyproject.toml").write_text('[tool.devx]\ndoc_coverage = "not-a-dict"\n')
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["--docs-dir", str(docs), "--source-dir", str(src)])
|
||||
assert result.exit_code == 0
|
||||
assert "100%" in result.output
|
||||
|
||||
@@ -19,6 +19,7 @@ from devx.ci.sync_wiki import (
|
||||
main,
|
||||
sync_files,
|
||||
transform_links,
|
||||
wiki_filename,
|
||||
)
|
||||
|
||||
|
||||
@@ -96,6 +97,29 @@ class TestGetWikiCloneUrl:
|
||||
assert "owner/repo.wiki.git" in url
|
||||
|
||||
|
||||
class TestWikiFilename:
|
||||
def test_no_dashes(self) -> None:
|
||||
assert wiki_filename("Home") == "Home.md"
|
||||
|
||||
def test_single_word(self) -> None:
|
||||
assert wiki_filename("Architecture") == "Architecture.md"
|
||||
|
||||
def test_with_dashes_adds_marker(self) -> None:
|
||||
assert wiki_filename("Getting-Started") == "Getting-Started.-.md"
|
||||
|
||||
def test_spaces_become_dashes_with_marker(self) -> None:
|
||||
# "Getting Started" → "Getting-Started" (has dash) → marker added
|
||||
assert wiki_filename("Getting Started") == "Getting-Started.-.md"
|
||||
|
||||
def test_spaces_no_dashes_no_marker(self) -> None:
|
||||
# "Foo Bar" → "Foo-Bar" (has dash) → marker added
|
||||
assert wiki_filename("Foo Bar") == "Foo-Bar.-.md"
|
||||
|
||||
def test_single_word_with_spaces_no_dash(self) -> None:
|
||||
# No dash at all after conversion → no marker
|
||||
assert wiki_filename("HelloWorld") == "HelloWorld.md"
|
||||
|
||||
|
||||
class TestCloneWiki:
|
||||
@patch("devx.ci.sync_wiki.subprocess.run")
|
||||
def test_clone_success(self, mock_run: MagicMock, tmp_path: Path) -> None:
|
||||
@@ -208,6 +232,8 @@ class TestCommitAndPush:
|
||||
mock_run.side_effect = [
|
||||
MagicMock(returncode=0), # git add
|
||||
MagicMock(returncode=1), # git diff --cached --quiet (has changes)
|
||||
MagicMock(returncode=0), # git config user.email
|
||||
MagicMock(returncode=0), # git config user.name
|
||||
MagicMock(returncode=0), # git commit
|
||||
MagicMock(returncode=0, stdout="", stderr=""), # git push
|
||||
]
|
||||
@@ -219,6 +245,8 @@ class TestCommitAndPush:
|
||||
mock_run.side_effect = [
|
||||
MagicMock(returncode=0), # git add
|
||||
MagicMock(returncode=1), # git diff --cached --quiet (has changes)
|
||||
MagicMock(returncode=0), # git config user.email
|
||||
MagicMock(returncode=0), # git config user.name
|
||||
MagicMock(returncode=0), # git commit
|
||||
MagicMock(returncode=1, stdout="", stderr="push failed"), # git push
|
||||
]
|
||||
@@ -319,6 +347,7 @@ class TestMain:
|
||||
assert result.exit_code == 0
|
||||
mock_init.assert_called_once()
|
||||
|
||||
@patch("devx.ci.sync_wiki.time.sleep")
|
||||
@patch("devx.ci.sync_wiki.clone_wiki")
|
||||
@patch("devx.ci.sync_wiki.commit_and_push", return_value=True)
|
||||
@patch("devx.ci.sync_wiki.sync_files", return_value=(1, 0))
|
||||
@@ -327,6 +356,7 @@ class TestMain:
|
||||
mock_sync: MagicMock,
|
||||
mock_push: MagicMock,
|
||||
mock_clone: MagicMock,
|
||||
mock_sleep: MagicMock,
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
@@ -376,6 +406,7 @@ class TestMain:
|
||||
assert result.exit_code == 0
|
||||
assert "No push needed" in result.output
|
||||
|
||||
@patch("devx.ci.sync_wiki.time.sleep")
|
||||
@patch("devx.ci.sync_wiki.clone_wiki", side_effect=[True, False])
|
||||
@patch("devx.ci.sync_wiki.commit_and_push", return_value=True)
|
||||
@patch("devx.ci.sync_wiki.sync_files", return_value=(1, 0))
|
||||
@@ -384,6 +415,7 @@ class TestMain:
|
||||
mock_sync: MagicMock,
|
||||
mock_push: MagicMock,
|
||||
mock_clone: MagicMock,
|
||||
mock_sleep: MagicMock,
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
@@ -400,6 +432,7 @@ class TestMain:
|
||||
assert result.exit_code != 0
|
||||
assert "could not clone" in result.output
|
||||
|
||||
@patch("devx.ci.sync_wiki.time.sleep")
|
||||
@patch("devx.ci.sync_wiki.clone_wiki")
|
||||
@patch("devx.ci.sync_wiki.commit_and_push", return_value=True)
|
||||
@patch("devx.ci.sync_wiki.sync_files", return_value=(1, 0))
|
||||
@@ -408,6 +441,7 @@ class TestMain:
|
||||
mock_sync: MagicMock,
|
||||
mock_push: MagicMock,
|
||||
mock_clone: MagicMock,
|
||||
mock_sleep: MagicMock,
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user