Public Access
DEVX-60: feat: add create-task, create-pr, pre-push-check tools and devx.mak fragment
Post-merge / detect-type (push) Successful in 6s
Post-merge / validate-commit-msg (push) Successful in 6s
Post-merge / configure-repo (push) Successful in 9s
Post-merge / release (push) Successful in 1m0s
Post-merge / vikunja (push) Successful in 14s
Post-merge / sync-wiki (push) Successful in 59s
Post-merge / badges (push) Successful in 1m12s
Post-merge / detect-type (push) Successful in 6s
Post-merge / validate-commit-msg (push) Successful in 6s
Post-merge / configure-repo (push) Successful in 9s
Post-merge / release (push) Successful in 1m0s
Post-merge / vikunja (push) Successful in 14s
Post-merge / sync-wiki (push) Successful in 59s
Post-merge / badges (push) Successful in 1m12s
This commit was merged in pull request #98.
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
"""devx — reusable development and CI/CD tools for oblachno-oss projects."""
|
||||
|
||||
__version__ = "0.14.2"
|
||||
__version__ = "0.15.0"
|
||||
|
||||
@@ -192,6 +192,21 @@ class GiteaClient:
|
||||
r = self._request("GET", f"/pulls/{pr_number}")
|
||||
return r.json()
|
||||
|
||||
def create_pr(self, title: str, head: str, base: str = "master", body: str = "") -> dict[str, Any]:
|
||||
"""Create a pull request and return the PR dict.
|
||||
|
||||
Args:
|
||||
title: PR title.
|
||||
head: Head branch name.
|
||||
base: Base branch name (default: master).
|
||||
body: PR description (markdown).
|
||||
"""
|
||||
payload: dict[str, Any] = {"title": title, "head": head, "base": base}
|
||||
if body:
|
||||
payload["body"] = body
|
||||
r = self._request("POST", "/pulls", json=payload)
|
||||
return r.json()
|
||||
|
||||
def list_prs(self, state: str = "all", **params: Any) -> list[dict[str, Any]]:
|
||||
"""List pull requests, optionally filtered by state.
|
||||
|
||||
@@ -353,6 +368,21 @@ class VikunjaClient:
|
||||
r = self._request("GET", f"/projects/{project_id}/tasks", params=params)
|
||||
return r.json()
|
||||
|
||||
def create_task(self, project_id: int, title: str, description: str = "") -> dict[str, Any]:
|
||||
"""Create a task in a project and return the created task dict.
|
||||
|
||||
Args:
|
||||
project_id: Target Vikunja project ID.
|
||||
title: Task title (required, non-empty).
|
||||
description: Task description (HTML supported, optional).
|
||||
"""
|
||||
r = self._request(
|
||||
"PUT",
|
||||
f"/projects/{project_id}/tasks",
|
||||
json={"title": title, "description": description},
|
||||
)
|
||||
return r.json()
|
||||
|
||||
def post_comment(self, task_id: int, comment: str) -> None:
|
||||
self._request("PUT", f"/tasks/{task_id}/comments", json={"comment": comment})
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# devx.mak — Shared Makefile fragment for devx-integrated projects.
|
||||
#
|
||||
# This fragment provides common targets for Vikunja task management,
|
||||
# PR creation, and pushing. It is designed to be included from a
|
||||
# project's Makefile after project-specific variables are set.
|
||||
#
|
||||
# Usage in your Makefile:
|
||||
#
|
||||
# # Set project-specific variables
|
||||
# DEVX_VIKUNJA_PROJECT_ID := 3
|
||||
# DEVX_REPO_OWNER := oblachno
|
||||
# DEVX_REPO_NAME := infra
|
||||
# DEVX_PYTHON := python3 # or $(BIN)/python, etc.
|
||||
#
|
||||
# # Include the devx fragment (silent if devx not installed yet)
|
||||
# DEVX_MAK := $(shell $(DEVX_PYTHON) -c \
|
||||
# "from pathlib import Path; import devx; print(Path(devx.__file__).parent / 'make' / 'devx.mak')" \
|
||||
# 2>/dev/null)
|
||||
# -include $(DEVX_MAK)
|
||||
#
|
||||
# The fragment uses ?= for all variables so projects can override them
|
||||
# before the include. If devx is not installed, the -include silently
|
||||
# skips and the targets are simply unavailable (run 'make setup' first).
|
||||
#
|
||||
# Variables:
|
||||
# DEVX_VIKUNJA_PROJECT_ID — Vikunja project ID (default: 1)
|
||||
# DEVX_REPO_OWNER — Gitea repository owner (default: empty)
|
||||
# DEVX_REPO_NAME — Gitea repository name (default: empty)
|
||||
# DEVX_PYTHON — Python executable (default: python3)
|
||||
# DEVX_PR_BASE — PR base branch (default: master)
|
||||
|
||||
DEVX_VIKUNJA_PROJECT_ID ?= 1
|
||||
DEVX_REPO_OWNER ?=
|
||||
DEVX_REPO_NAME ?=
|
||||
DEVX_PYTHON ?= python3
|
||||
DEVX_PR_BASE ?= master
|
||||
|
||||
.PHONY: devx-create-task devx-create-pr devx-push devx-push-with-pr
|
||||
|
||||
# Create a Vikunja task in the configured project
|
||||
devx-create-task:
|
||||
@$(DEVX_PYTHON) -m devx.tools.create_task --project-id $(DEVX_VIKUNJA_PROJECT_ID)
|
||||
|
||||
# Create a PR with title auto-derived from the Vikunja task
|
||||
devx-create-pr:
|
||||
@$(DEVX_PYTHON) -m devx.tools.create_pr \
|
||||
--owner $(DEVX_REPO_OWNER) \
|
||||
--repo $(DEVX_REPO_NAME) \
|
||||
--base $(DEVX_PR_BASE)
|
||||
|
||||
# Push current branch to origin
|
||||
devx-push:
|
||||
@git push -u origin HEAD
|
||||
|
||||
# Push and create PR in one step
|
||||
devx-push-with-pr: devx-push devx-create-pr
|
||||
@@ -0,0 +1,191 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Create a pull request with the correct title from the Vikunja task.
|
||||
|
||||
This tool is run **after** pushing a feature branch. It:
|
||||
|
||||
1. Extracts the task ID from the branch name (e.g. ``DEVX-31-fix-foo`` → ``DEVX-31``).
|
||||
2. Fetches the Vikunja task title for that task ID.
|
||||
3. Creates a PR with title ``{TASK_PREFIX}-N: <vikunja task title>``.
|
||||
|
||||
This eliminates manual PR title entry and ensures the title always
|
||||
matches the Vikunja task — which is what the auto-merge workflow
|
||||
validates.
|
||||
|
||||
If a PR already exists for the branch, the tool prints its URL and
|
||||
exits successfully (idempotent).
|
||||
|
||||
Usage::
|
||||
|
||||
python -m devx.tools.create_pr --branch DEVX-31-fix-foo
|
||||
|
||||
The repository is auto-detected from ``DEVX_REPO_OWNER`` /
|
||||
``DEVX_REPO_NAME`` or ``GITHUB_REPOSITORY`` environment variables.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess # nosec B404
|
||||
|
||||
import click
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from devx.api_clients import GiteaClient, VikunjaClient
|
||||
from devx.config import (
|
||||
DEFAULT_PER_PAGE,
|
||||
GITEA_API_URL,
|
||||
REPO_OWNER,
|
||||
TASK_ID_RE,
|
||||
TASK_PREFIX,
|
||||
VIKUNJA_API_URL,
|
||||
VIKUNJA_PROJECT_ID,
|
||||
)
|
||||
from devx.i18n import _
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
def get_repo_name() -> str:
|
||||
"""Auto-detect repository name from env vars or git remote."""
|
||||
name = os.environ.get("DEVX_REPO_NAME", "")
|
||||
if name:
|
||||
return name
|
||||
github_repo = os.environ.get("GITHUB_REPOSITORY", "")
|
||||
if github_repo and "/" in github_repo:
|
||||
return github_repo.split("/", 1)[1]
|
||||
raise click.ClickException(
|
||||
_("Repository name not set. Use DEVX_REPO_NAME or GITHUB_REPOSITORY env var."),
|
||||
)
|
||||
|
||||
|
||||
def extract_task_id(branch: str) -> str:
|
||||
"""Extract the task ID (e.g. ``DEVX-31``) from a branch name."""
|
||||
match = TASK_ID_RE.search(branch)
|
||||
return match.group(0) if match else ""
|
||||
|
||||
|
||||
def get_vikunja_task_title(task_id: str) -> str:
|
||||
"""Fetch the Vikunja task title for the given task identifier.
|
||||
|
||||
Raises ClickException if VIKUNJA_TOKEN is not set or the task is not found.
|
||||
"""
|
||||
token = os.environ.get("VIKUNJA_TOKEN", "")
|
||||
if not token:
|
||||
raise click.ClickException(_("VIKUNJA_TOKEN is not set. Required to derive PR title."))
|
||||
client = VikunjaClient(VIKUNJA_API_URL, token)
|
||||
page = 1
|
||||
while True:
|
||||
tasks = client.list_project_tasks(VIKUNJA_PROJECT_ID, page=page, per_page=DEFAULT_PER_PAGE)
|
||||
if not tasks:
|
||||
break
|
||||
matches = [t for t in tasks if t.get("identifier") == task_id]
|
||||
if matches:
|
||||
return str(matches[0].get("title", ""))
|
||||
if len(tasks) < DEFAULT_PER_PAGE:
|
||||
break
|
||||
page += 1
|
||||
raise click.ClickException(
|
||||
_(
|
||||
"Could not find Vikunja task {task_id} in project {project_id}.",
|
||||
task_id=task_id,
|
||||
project_id=VIKUNJA_PROJECT_ID,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def find_existing_pr(client: GiteaClient, branch: str) -> dict | None:
|
||||
"""Return an existing open PR for the branch, or None."""
|
||||
prs = client.list_prs(state="open")
|
||||
for pr in prs:
|
||||
if pr.get("head", {}).get("ref") == branch:
|
||||
return pr
|
||||
return None
|
||||
|
||||
|
||||
def create_pr(
|
||||
branch: str,
|
||||
base: str,
|
||||
body: str,
|
||||
repo_owner: str,
|
||||
repo_name: str,
|
||||
) -> dict:
|
||||
"""Create a PR with the title derived from the Vikunja task.
|
||||
|
||||
Returns the PR dict from the Gitea API.
|
||||
"""
|
||||
task_id = extract_task_id(branch)
|
||||
if not task_id:
|
||||
raise click.ClickException(
|
||||
_(
|
||||
"Branch '{branch}' does not contain a task ID.\n Expected format: {prefix}-N-short-description",
|
||||
branch=branch,
|
||||
prefix=TASK_PREFIX,
|
||||
),
|
||||
)
|
||||
|
||||
token = os.environ.get("REPO_TOKEN", "")
|
||||
if not token:
|
||||
raise click.ClickException(_("REPO_TOKEN is not set. Required to create a PR."))
|
||||
|
||||
vikunja_title = get_vikunja_task_title(task_id)
|
||||
pr_title = f"{task_id}: {vikunja_title}"
|
||||
|
||||
client = GiteaClient(GITEA_API_URL, token, repo_owner, repo_name)
|
||||
|
||||
existing = find_existing_pr(client, branch)
|
||||
if existing:
|
||||
click.echo(
|
||||
_(
|
||||
"PR already exists: #{index} — {url}",
|
||||
index=existing.get("number", "?"),
|
||||
url=existing.get("html_url", ""),
|
||||
),
|
||||
)
|
||||
return existing
|
||||
|
||||
pr = client.create_pr(title=pr_title, head=branch, base=base, body=body)
|
||||
click.echo(
|
||||
_(
|
||||
"Created PR #{index}: {title}\n {url}",
|
||||
index=pr.get("number", "?"),
|
||||
title=pr_title,
|
||||
url=pr.get("html_url", ""),
|
||||
),
|
||||
)
|
||||
return pr
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("--branch", default=None, help="Head branch (default: auto-detect from git).")
|
||||
@click.option("--base", default="master", show_default=True, help="Base branch.")
|
||||
@click.option("--body", default="", help="PR body (markdown). Read from stdin if '-' is passed.")
|
||||
@click.option("--owner", default=None, help="Repository owner (default: DEVX_REPO_OWNER).")
|
||||
@click.option("--repo", default=None, help="Repository name (default: DEVX_REPO_NAME or GITHUB_REPOSITORY).")
|
||||
def cli(branch: str | None, base: str, body: str, owner: str | None, repo: str | None) -> None:
|
||||
"""Create a PR with the correct title from the Vikunja task."""
|
||||
if branch is None:
|
||||
result = subprocess.run( # nosec
|
||||
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
raise click.ClickException(
|
||||
_("Could not detect current branch: {error}", error=result.stderr.strip()),
|
||||
)
|
||||
branch = result.stdout.strip()
|
||||
|
||||
if body == "-":
|
||||
body = click.get_text_stream("stdin").read().strip()
|
||||
|
||||
repo_owner = owner or REPO_OWNER
|
||||
if not repo_owner:
|
||||
raise click.ClickException(_("Repository owner not set. Use --owner or DEVX_REPO_OWNER env var."))
|
||||
repo_name = repo or get_repo_name()
|
||||
|
||||
create_pr(branch, base, body, repo_owner, repo_name)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
cli() # pragma: no cover
|
||||
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Create a Vikunja task with a detailed HTML description.
|
||||
|
||||
This tool is used during the planning phase of the development workflow
|
||||
to create a well-described task before any code is written. The task
|
||||
identifier (e.g. ``DEVX-N``, ``GRM-N``, ``OBL-INFRA-N``) is then used
|
||||
to name the feature branch and the pull request.
|
||||
|
||||
Usage::
|
||||
|
||||
python -m devx.tools.create_task --title "Add release automation" \\
|
||||
--description "<h2>Overview</h2><p>Implement automated...</p>"
|
||||
|
||||
The project ID and task prefix are read from ``DEVX_VIKUNJA_PROJECT_ID``
|
||||
and ``DEVX_TASK_PREFIX`` environment variables (or ``.env``).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
import click
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from devx.api_clients import VikunjaClient
|
||||
from devx.config import TASK_PREFIX, VIKUNJA_API_URL, VIKUNJA_PROJECT_ID
|
||||
from devx.i18n import _
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("--title", required=True, help="Task title (becomes the Vikunja task title).")
|
||||
@click.option(
|
||||
"--description",
|
||||
default="",
|
||||
help="Task description (HTML supported). Read from stdin if '-' is passed.",
|
||||
)
|
||||
@click.option("--project-id", type=int, default=None, help="Vikunja project ID (default: DEVX_VIKUNJA_PROJECT_ID).")
|
||||
def cli(title: str, description: str, project_id: int | None) -> None:
|
||||
"""Create a Vikunja task and print its identifier."""
|
||||
token = os.environ.get("VIKUNJA_TOKEN", "")
|
||||
if not token:
|
||||
raise click.ClickException(_("VIKUNJA_TOKEN is not set. Set it in .env or environment."))
|
||||
|
||||
pid = project_id if project_id is not None else VIKUNJA_PROJECT_ID
|
||||
|
||||
if description == "-":
|
||||
description = click.get_text_stream("stdin").read().strip()
|
||||
|
||||
client = VikunjaClient(VIKUNJA_API_URL, token)
|
||||
task = client.create_task(pid, title, description)
|
||||
|
||||
identifier = task.get("identifier", "")
|
||||
task_id = task.get("id", "")
|
||||
click.echo(
|
||||
_(
|
||||
"Created Vikunja task: {identifier} (id={task_id})",
|
||||
identifier=identifier,
|
||||
task_id=task_id,
|
||||
)
|
||||
)
|
||||
if identifier:
|
||||
click.echo(
|
||||
_(
|
||||
"Next steps:\n"
|
||||
" 1. git checkout master && git pull\n"
|
||||
" 2. git checkout -b {prefix}-{num}-short-description\n"
|
||||
" 3. Implement changes, commit with conventional commit format\n"
|
||||
" 4. git push -u origin HEAD\n"
|
||||
" 5. make create-pr (creates PR with title: {identifier}: {title})",
|
||||
prefix=TASK_PREFIX,
|
||||
num=identifier.split("-")[-1] if "-" in identifier else "N",
|
||||
identifier=identifier,
|
||||
title=title,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
cli() # pragma: no cover
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Pre-push validation: ensure a Vikunja task exists for the branch.
|
||||
|
||||
This tool is designed to run as a git pre-push hook. It extracts the
|
||||
task ID from the branch name (e.g. ``DEVX-31-fix-foo`` → ``DEVX-31``)
|
||||
and verifies that a corresponding Vikunja task exists.
|
||||
|
||||
If the task does not exist, the hook **fails with guidance** — it does
|
||||
not auto-create the task. This prevents accidental pushes of branches
|
||||
without a planning task.
|
||||
|
||||
Usage::
|
||||
|
||||
python -m devx.tools.pre_push_check --branch DEVX-31-fix-foo
|
||||
|
||||
Exit codes:
|
||||
0 — all checks passed, safe to push
|
||||
1 — validation failed (missing task, missing token, etc.)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess # nosec B404
|
||||
|
||||
import click
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from devx.api_clients import VikunjaClient
|
||||
from devx.config import DEFAULT_PER_PAGE, TASK_ID_RE, TASK_PREFIX, VIKUNJA_API_URL, VIKUNJA_PROJECT_ID
|
||||
from devx.i18n import _
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
def get_current_branch() -> str:
|
||||
"""Return the current git branch name, or empty string on error."""
|
||||
result = subprocess.run( # nosec
|
||||
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def extract_task_id(branch: str) -> str:
|
||||
"""Extract the task ID (e.g. ``DEVX-31``) from a branch name."""
|
||||
match = TASK_ID_RE.search(branch)
|
||||
return match.group(0) if match else ""
|
||||
|
||||
|
||||
def task_exists(task_id: str) -> bool:
|
||||
"""Check if a Vikunja task with the given identifier exists.
|
||||
|
||||
Returns ``False`` if VIKUNJA_TOKEN is not set (soft-fail in local mode).
|
||||
"""
|
||||
token = os.environ.get("VIKUNJA_TOKEN", "")
|
||||
if not token:
|
||||
return False
|
||||
client = VikunjaClient(VIKUNJA_API_URL, token)
|
||||
page = 1
|
||||
while True:
|
||||
tasks = client.list_project_tasks(VIKUNJA_PROJECT_ID, page=page, per_page=DEFAULT_PER_PAGE)
|
||||
if not tasks:
|
||||
break
|
||||
if any(t.get("identifier") == task_id for t in tasks):
|
||||
return True
|
||||
if len(tasks) < DEFAULT_PER_PAGE:
|
||||
break
|
||||
page += 1
|
||||
return False
|
||||
|
||||
|
||||
def validate(branch: str) -> None:
|
||||
"""Run all pre-push validations for the given branch.
|
||||
|
||||
Raises ``click.ClickException`` on failure.
|
||||
"""
|
||||
if not branch or branch in ("master", "main"):
|
||||
return
|
||||
|
||||
task_id = extract_task_id(branch)
|
||||
if not task_id:
|
||||
raise click.ClickException(
|
||||
_(
|
||||
"Branch '{branch}' does not contain a task ID.\n"
|
||||
" Expected format: {prefix}-N-short-description\n"
|
||||
" Example: {prefix}-42-add-feature\n"
|
||||
" Fix: rename the branch or create a Vikunja task first:\n"
|
||||
' python -m devx.tools.create_task --title "Task title"',
|
||||
branch=branch,
|
||||
prefix=TASK_PREFIX,
|
||||
)
|
||||
)
|
||||
|
||||
token = os.environ.get("VIKUNJA_TOKEN", "")
|
||||
if not token:
|
||||
click.echo(
|
||||
_(
|
||||
"WARNING: VIKUNJA_TOKEN not set — skipping task existence check. "
|
||||
"Set it in .env to enable full validation.",
|
||||
),
|
||||
err=True,
|
||||
)
|
||||
return
|
||||
|
||||
if not task_exists(task_id):
|
||||
raise click.ClickException(
|
||||
_(
|
||||
"Vikunja task {task_id} not found in project {project_id}.\n"
|
||||
" Create it first:\n"
|
||||
' python -m devx.tools.create_task --title "Task title"\n'
|
||||
" Or check that the task ID in the branch name is correct.",
|
||||
task_id=task_id,
|
||||
project_id=VIKUNJA_PROJECT_ID,
|
||||
)
|
||||
)
|
||||
|
||||
click.echo(_("Pre-push check passed: task {task_id} exists.", task_id=task_id))
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("--branch", default=None, help="Branch name (default: auto-detect from git).")
|
||||
def cli(branch: str | None) -> None:
|
||||
"""Validate pre-push preconditions for the current branch."""
|
||||
if branch is None:
|
||||
branch = get_current_branch()
|
||||
validate(branch)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
cli() # pragma: no cover
|
||||
+12
-2
@@ -150,19 +150,29 @@ def _verify(bin_dir: str) -> None:
|
||||
default=False,
|
||||
help="Skip Ansible Galaxy collection installation.",
|
||||
)
|
||||
@click.option(
|
||||
"--skip-install",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Skip pip install (use when deps already installed, e.g. devx came via ci extra).",
|
||||
)
|
||||
def main(
|
||||
bin_dir: str,
|
||||
extras: str,
|
||||
no_pre_commit: bool,
|
||||
no_tea_login: bool,
|
||||
no_ansible_collections: bool,
|
||||
skip_install: bool,
|
||||
) -> None:
|
||||
"""Install Python deps, pre-commit hooks, and configure tea CLI."""
|
||||
if not Path(bin_dir).exists():
|
||||
raise click.ClickException(f"Bin directory not found: {bin_dir}. Run 'python3 -m venv .venv' first.")
|
||||
|
||||
click.echo(f"Installing Python dependencies (extras: {extras})...")
|
||||
_install_python_deps(bin_dir, extras)
|
||||
if not skip_install:
|
||||
click.echo(f"Installing Python dependencies (extras: {extras})...")
|
||||
_install_python_deps(bin_dir, extras)
|
||||
else:
|
||||
click.echo("Skipping pip install (--skip-install).")
|
||||
|
||||
if not no_ansible_collections:
|
||||
click.echo("Installing Ansible Galaxy collections...")
|
||||
|
||||
@@ -1470,5 +1470,133 @@
|
||||
"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."
|
||||
},
|
||||
"Branch '{branch}' does not contain a task ID.\n Expected format: {prefix}-N-short-description": {
|
||||
"bg": "Клон '{branch}' не съдържа ID на задача.\n Очакван формат: {prefix}-N-кратко-описание",
|
||||
"de": "Branch '{branch}' enthält keine Task-ID.\n Erwartetes Format: {prefix}-N-kurz-beschreibung",
|
||||
"en": "Branch '{branch}' does not contain a task ID.\n Expected format: {prefix}-N-short-description",
|
||||
"pl": "Gałąź '{branch}' nie zawiera ID zadania.\n Oczekiwany format: {prefix}-N-krótki-opis",
|
||||
"ru": "Ветка '{branch}' не содержит ID задачи.\n Ожидаемый формат: {prefix}-N-краткое-описание",
|
||||
"zh": "分支 '{branch}' 不包含任务 ID。\n 预期格式: {prefix}-N-简短描述"
|
||||
},
|
||||
"Branch '{branch}' does not contain a task ID.\n Expected format: {prefix}-N-short-description\n Example: {prefix}-42-add-feature\n Fix: rename the branch or create a Vikunja task first:\n python -m devx.tools.create_task --title \"Task title\"": {
|
||||
"bg": "Клон '{branch}' не съдържа ID на задача.\n Очакван формат: {prefix}-N-кратко-описание\n Пример: {prefix}-42-add-feature\n Решение: преименувайте клона или създайте Vikunja задача:\n python -m devx.tools.create_task --title \"Заглавие на задача\"",
|
||||
"de": "Branch '{branch}' enthält keine Task-ID.\n Erwartetes Format: {prefix}-N-kurz-beschreibung\n Beispiel: {prefix}-42-add-feature\n Fix: Branch umbenennen oder Vikunja-Task erstellen:\n python -m devx.tools.create_task --title \"Task-Titel\"",
|
||||
"en": "Branch '{branch}' does not contain a task ID.\n Expected format: {prefix}-N-short-description\n Example: {prefix}-42-add-feature\n Fix: rename the branch or create a Vikunja task first:\n python -m devx.tools.create_task --title \"Task title\"",
|
||||
"pl": "Gałąź '{branch}' nie zawiera ID zadania.\n Oczekiwany format: {prefix}-N-krótki-opis\n Przykład: {prefix}-42-add-feature\n Naprawa: zmień nazwę gałęzi lub utwórz zadanie Vikunja:\n python -m devx.tools.create_task --title \"Tytuł zadania\"",
|
||||
"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 \"任务标题\""
|
||||
},
|
||||
"Could not find Vikunja task {task_id} in project {project_id}.": {
|
||||
"bg": "Не е намерена Vikunja задача {task_id} в проект {project_id}.",
|
||||
"de": "Vikunja-Task {task_id} in Projekt {project_id} nicht gefunden.",
|
||||
"en": "Could not find Vikunja task {task_id} in project {project_id}.",
|
||||
"pl": "Nie znaleziono zadania Vikunja {task_id} w projekcie {project_id}.",
|
||||
"ru": "Не найдена задача Vikunja {task_id} в проекте {project_id}.",
|
||||
"zh": "在项目 {project_id} 中找不到 Vikunja 任务 {task_id}。"
|
||||
},
|
||||
"Could not detect current branch: {error}": {
|
||||
"bg": "Не може да се определи текущия клон: {error}",
|
||||
"de": "Aktueller Branch konnte nicht erkannt werden: {error}",
|
||||
"en": "Could not detect current branch: {error}",
|
||||
"pl": "Nie można wykryć bieżącej gałęzi: {error}",
|
||||
"ru": "Не удалось определить текущую ветку: {error}",
|
||||
"zh": "无法检测当前分支: {error}"
|
||||
},
|
||||
"Created PR #{index}: {title}\n {url}": {
|
||||
"bg": "Създаден PR #{index}: {title}\n {url}",
|
||||
"de": "PR erstellt #{index}: {title}\n {url}",
|
||||
"en": "Created PR #{index}: {title}\n {url}",
|
||||
"pl": "Utworzono PR #{index}: {title}\n {url}",
|
||||
"ru": "Создан PR #{index}: {title}\n {url}",
|
||||
"zh": "已创建 PR #{index}: {title}\n {url}"
|
||||
},
|
||||
"Created Vikunja task: {identifier} (id={task_id})": {
|
||||
"bg": "Създадена Vikunja задача: {identifier} (id={task_id})",
|
||||
"de": "Vikunja-Task erstellt: {identifier} (id={task_id})",
|
||||
"en": "Created Vikunja task: {identifier} (id={task_id})",
|
||||
"pl": "Utworzono zadanie Vikunja: {identifier} (id={task_id})",
|
||||
"ru": "Создана задача Vikunja: {identifier} (id={task_id})",
|
||||
"zh": "已创建 Vikunja 任务: {identifier} (id={task_id})"
|
||||
},
|
||||
"Next steps:\n 1. git checkout master && git pull\n 2. git checkout -b {prefix}-{num}-short-description\n 3. Implement changes, commit with conventional commit format\n 4. git push -u origin HEAD\n 5. make create-pr (creates PR with title: {identifier}: {title})": {
|
||||
"bg": "Следващи стъпки:\n 1. git checkout master && git pull\n 2. git checkout -b {prefix}-{num}-кратко-описание\n 3. Имплементирайте промените, commit с conventional commit формат\n 4. git push -u origin HEAD\n 5. make create-pr (създава PR с заглавие: {identifier}: {title})",
|
||||
"de": "Nächste Schritte:\n 1. git checkout master && git pull\n 2. git checkout -b {prefix}-{num}-kurz-beschreibung\n 3. Änderungen implementieren, mit Conventional-Commit-Format committen\n 4. git push -u origin HEAD\n 5. make create-pr (erstellt PR mit Titel: {identifier}: {title})",
|
||||
"en": "Next steps:\n 1. git checkout master && git pull\n 2. git checkout -b {prefix}-{num}-short-description\n 3. Implement changes, commit with conventional commit format\n 4. git push -u origin HEAD\n 5. make create-pr (creates PR with title: {identifier}: {title})",
|
||||
"pl": "Następne kroki:\n 1. git checkout master && git pull\n 2. git checkout -b {prefix}-{num}-krótki-opis\n 3. Wprowadź zmiany, commituj w formacie conventional commit\n 4. git push -u origin HEAD\n 5. make create-pr (tworzy PR z tytułem: {identifier}: {title})",
|
||||
"ru": "Следующие шаги:\n 1. git checkout master && git pull\n 2. git checkout -b {prefix}-{num}-краткое-описание\n 3. Реализуйте изменения, коммитьте в conventional commit формате\n 4. git push -u origin HEAD\n 5. make create-pr (создаёт PR с заголовком: {identifier}: {title})",
|
||||
"zh": "后续步骤:\n 1. git checkout master && git pull\n 2. git checkout -b {prefix}-{num}-简短描述\n 3. 实现更改,使用 conventional commit 格式提交\n 4. git push -u origin HEAD\n 5. make create-pr (创建 PR,标题: {identifier}: {title})"
|
||||
},
|
||||
"PR already exists: #{index} — {url}": {
|
||||
"bg": "PR вече съществува: #{index} — {url}",
|
||||
"de": "PR existiert bereits: #{index} — {url}",
|
||||
"en": "PR already exists: #{index} — {url}",
|
||||
"pl": "PR już istnieje: #{index} — {url}",
|
||||
"ru": "PR уже существует: #{index} — {url}",
|
||||
"zh": "PR 已存在: #{index} — {url}"
|
||||
},
|
||||
"Pre-push check passed: task {task_id} exists.": {
|
||||
"bg": "Pre-push проверката премина: задача {task_id} съществува.",
|
||||
"de": "Pre-push-Prüfung bestanden: Task {task_id} existiert.",
|
||||
"en": "Pre-push check passed: task {task_id} exists.",
|
||||
"pl": "Sprawdzanie pre-push zakończone: zadanie {task_id} istnieje.",
|
||||
"ru": "Pre-push проверка пройдена: задача {task_id} существует.",
|
||||
"zh": "Pre-push 检查通过: 任务 {task_id} 存在。"
|
||||
},
|
||||
"REPO_TOKEN is not set. Required to create a PR.": {
|
||||
"bg": "REPO_TOKEN не е зададен. Необходим за създаване на PR.",
|
||||
"de": "REPO_TOKEN nicht gesetzt. Erforderlich zum Erstellen eines PR.",
|
||||
"en": "REPO_TOKEN is not set. Required to create a PR.",
|
||||
"pl": "REPO_TOKEN nie jest ustawiony. Wymagany do utworzenia PR.",
|
||||
"ru": "REPO_TOKEN не установлен. Требуется для создания PR.",
|
||||
"zh": "REPO_TOKEN 未设置。创建 PR 所需。"
|
||||
},
|
||||
"Repository name not set. Use DEVX_REPO_NAME or GITHUB_REPOSITORY env var.": {
|
||||
"bg": "Името на хранилището не е зададено. Използвайте DEVX_REPO_NAME или GITHUB_REPOSITORY env var.",
|
||||
"de": "Repository-Name nicht gesetzt. Verwende DEVX_REPO_NAME oder GITHUB_REPOSITORY env var.",
|
||||
"en": "Repository name not set. Use DEVX_REPO_NAME or GITHUB_REPOSITORY env var.",
|
||||
"pl": "Nazwa repozytorium nie jest ustawiona. Użyj DEVX_REPO_NAME lub GITHUB_REPOSITORY env var.",
|
||||
"ru": "Имя репозитория не установлено. Используйте DEVX_REPO_NAME или GITHUB_REPOSITORY env var.",
|
||||
"zh": "仓库名称未设置。使用 DEVX_REPO_NAME 或 GITHUB_REPOSITORY 环境变量。"
|
||||
},
|
||||
"Repository owner not set. Use --owner or DEVX_REPO_OWNER env var.": {
|
||||
"bg": "Собственикът на хранилището не е зададен. Използвайте --owner или DEVX_REPO_OWNER env var.",
|
||||
"de": "Repository-Owner nicht gesetzt. Verwende --owner oder DEVX_REPO_OWNER env var.",
|
||||
"en": "Repository owner not set. Use --owner or DEVX_REPO_OWNER env var.",
|
||||
"pl": "Właściciel repozytorium nie jest ustawiony. Użyj --owner lub DEVX_REPO_OWNER env var.",
|
||||
"ru": "Владелец репозитория не установлен. Используйте --owner или DEVX_REPO_OWNER env var.",
|
||||
"zh": "仓库所有者未设置。使用 --owner 或 DEVX_REPO_OWNER 环境变量。"
|
||||
},
|
||||
"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.",
|
||||
"en": "VIKUNJA_TOKEN is not set. Required to derive PR title.",
|
||||
"pl": "VIKUNJA_TOKEN nie jest ustawiony. Wymagany do pobrania tytułu PR.",
|
||||
"ru": "VIKUNJA_TOKEN не установлен. Требуется для получения заголовка PR.",
|
||||
"zh": "VIKUNJA_TOKEN 未设置。推导 PR 标题所需。"
|
||||
},
|
||||
"VIKUNJA_TOKEN is not set. Set it in .env or environment.": {
|
||||
"bg": "VIKUNJA_TOKEN не е зададен. Задайте го в .env или средата.",
|
||||
"de": "VIKUNJA_TOKEN nicht gesetzt. In .env oder Umgebung setzen.",
|
||||
"en": "VIKUNJA_TOKEN is not set. Set it in .env or environment.",
|
||||
"pl": "VIKUNJA_TOKEN nie jest ustawiony. Ustaw go w .env lub środowisku.",
|
||||
"ru": "VIKUNJA_TOKEN не установлен. Установите его в .env или среде.",
|
||||
"zh": "VIKUNJA_TOKEN 未设置。在 .env 或环境中设置它。"
|
||||
},
|
||||
"Vikunja task {task_id} not found in project {project_id}.\n Create it first:\n python -m devx.tools.create_task --title \"Task title\"\n Or check that the task ID in the branch name is correct.": {
|
||||
"bg": "Vikunja задача {task_id} не е намерена в проект {project_id}.\n Създайте я първо:\n python -m devx.tools.create_task --title \"Заглавие на задача\"\n Или проверете че ID на задачата в името на клона е правилно.",
|
||||
"de": "Vikunja-Task {task_id} in Projekt {project_id} nicht gefunden.\n Zuerst erstellen:\n python -m devx.tools.create_task --title \"Task-Titel\"\n Oder prüfen, ob die Task-ID im Branch-Namen korrekt ist.",
|
||||
"en": "Vikunja task {task_id} not found in project {project_id}.\n Create it first:\n python -m devx.tools.create_task --title \"Task title\"\n Or check that the task ID in the branch name is correct.",
|
||||
"pl": "Zadanie Vikunja {task_id} nie znalezione w projekcie {project_id}.\n Utwórz je najpierw:\n python -m devx.tools.create_task --title \"Tytuł zadania\"\n Lub sprawdź, czy ID zadania w nazwie gałęzi jest poprawne.",
|
||||
"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 是否正确。"
|
||||
},
|
||||
"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.",
|
||||
"en": "WARNING: VIKUNJA_TOKEN not set — skipping task existence check. Set it in .env to enable full validation.",
|
||||
"pl": "OSTRZEŻENIE: VIKUNJA_TOKEN nie jest ustawiony — pomijanie sprawdzania istnienia zadania. Ustaw w .env, aby włączyć pełną walidację.",
|
||||
"ru": "ПРЕДУПРЕЖДЕНИЕ: VIKUNJA_TOKEN не установлен — пропуск проверки существования задачи. Установите в .env для полной проверки.",
|
||||
"zh": "警告: VIKUNJA_TOKEN 未设置 — 跳过任务存在性检查。在 .env 中设置以启用完整验证。"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user