DEVX-125: Detect double-prefix in Vikunja task title during pre-merge validation #189

Merged
emil merged 1 commits from DEVX-125-detect-vikunja-double-prefix into master 2026-07-11 23:07:46 +00:00
4 changed files with 71 additions and 26 deletions
+6
View File
@@ -160,6 +160,12 @@ The following rules are enforced for `master`:
### 1. Create Vikunja Task
Create a task in Vikunja to get a `DEVX-N` identifier.
**IMPORTANT:** The task title must NOT include the `DEVX-N:` prefix.
The `make create-pr` and `check_auto_merge_ready` commands automatically
prepend `DEVX-N: ` to the Vikunja task title when forming the PR title.
If the Vikunja task title already includes the prefix, the PR title will
have a double prefix and auto-merge validation will fail.
### 2. Create Branch
```bash
git checkout master && git pull
+22 -6
View File
@@ -241,17 +241,33 @@ def cli(
else:
click.echo("[pre-merge-check] WARNING: VIKUNJA_TOKEN not set — skipping Vikunja title match check.")
else:
expected = f"{task_id}: {vikunja_title}"
if pr_title != expected:
# Defensive check: warn if the Vikunja task title already includes
# the task ID prefix. The expected PR title is
# f"{task_id}: {vikunja_title}" — if vikunja_title already starts
# with "{task_id}:", the PR title will have a double prefix.
if vikunja_title.startswith(f"{task_id}:"):
errors.append(
_(
"PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {title}",
expected=expected,
title=pr_title,
"Vikunja task title '{title}' starts with '{prefix}:'. "
"The task title should NOT include the '{prefix}' prefix — "
"it is automatically added to the PR title. "
"Update the Vikunja task title to remove the prefix.",
title=vikunja_title,
prefix=task_id,
),
)
else:
click.echo(f"[pre-merge-check] Vikunja title match OK: {expected}")
expected = f"{task_id}: {vikunja_title}"
if pr_title != expected:
errors.append(
_(
"PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {title}",
expected=expected,
title=pr_title,
),
)
else:
click.echo(f"[pre-merge-check] Vikunja title match OK: {expected}")
# 6. Branch behind master (skip if --skip-behind-check)
if not skip_behind_check:
+28 -20
View File
@@ -3568,35 +3568,43 @@
"zh": "{separator}"
},
"Allow empty tag (PR mode where SHA is concrete).": {
"bg": "Allow empty tag (PR mode where SHA is concrete).",
"de": "Allow empty tag (PR mode where SHA is concrete).",
"bg": "Позволи празен таг (PR режим, където SHA е конкретен).",
"de": "Leeren Tag zulassen (PR-Modus, in dem SHA konkret ist).",
"en": "Allow empty tag (PR mode where SHA is concrete).",
"pl": "Allow empty tag (PR mode where SHA is concrete).",
"ru": "Allow empty tag (PR mode where SHA is concrete).",
"zh": "Allow empty tag (PR mode where SHA is concrete)."
"pl": "Zezwalaj na pusty tag (tryb PR, w którym SHA jest konkretne).",
"ru": "Разрешить пустой тег (режим PR, где SHA конкретен).",
"zh": "允许空标签(SHA 为具体值的 PR 模式)。"
},
"Git tag or ref that was deployed": {
"bg": "Git tag or ref that was deployed",
"de": "Git tag or ref that was deployed",
"bg": "Git таг или референция, която беше разгърната",
"de": "Git-Tag oder Ref, der bereitgestellt wurde",
"en": "Git tag or ref that was deployed",
"pl": "Git tag or ref that was deployed",
"ru": "Git tag or ref that was deployed",
"zh": "Git tag or ref that was deployed"
"pl": "Tag Git lub ref, który został wdrożony",
"ru": "Git-тег или ссылка, которые были развёрнуты",
"zh": "已部署的 Git 标签或引用"
},
"Git tag to deploy (e.g. v0.28.1).": {
"bg": "Git tag to deploy (e.g. v0.28.1).",
"de": "Git tag to deploy (e.g. v0.28.1).",
"bg": "Git таг за разгръщане (напр. v0.28.1).",
"de": "Git-Tag für Bereitstellung (z.B. v0.28.1).",
"en": "Git tag to deploy (e.g. v0.28.1).",
"pl": "Git tag to deploy (e.g. v0.28.1).",
"ru": "Git tag to deploy (e.g. v0.28.1).",
"zh": "Git tag to deploy (e.g. v0.28.1)."
"pl": "Tag Git do wdrożenia (np. v0.28.1).",
"ru": "Git-тег для развёртывания (напр. v0.28.1).",
"zh": "要部署的 Git 标签(例如 v0.28.1)。"
},
"Write deploy-ref to $GITHUB_OUTPUT file.": {
"bg": "Write deploy-ref to $GITHUB_OUTPUT file.",
"de": "Write deploy-ref to $GITHUB_OUTPUT file.",
"bg": "Запиши deploy-ref в $GITHUB_OUTPUT файла.",
"de": "Deploy-ref in $GITHUB_OUTPUT-Datei schreiben.",
"en": "Write deploy-ref to $GITHUB_OUTPUT file.",
"pl": "Write deploy-ref to $GITHUB_OUTPUT file.",
"ru": "Write deploy-ref to $GITHUB_OUTPUT file.",
"zh": "Write deploy-ref to $GITHUB_OUTPUT file."
"pl": "Zapisz deploy-ref do pliku $GITHUB_OUTPUT.",
"ru": "Записать deploy-ref в файл $GITHUB_OUTPUT.",
"zh": " deploy-ref 写入 $GITHUB_OUTPUT 文件。"
},
"Vikunja task title '{title}' starts with '{prefix}:'. The task title should NOT include the '{prefix}' prefix — it is automatically added to the PR title. Update the Vikunja task title to remove the prefix.": {
"bg": "Заглавието на задачата във Vikunja '{title}' започва с '{prefix}:'. Заглавието на задачата НЕ трябва да съдържа префикса '{prefix}' — той се добавя автоматично към заглавието на PR. Актуализирайте заглавието на задачата във Vikunja, за да премахнете префикса.",
"de": "Der Vikunja-Aufgabentitel '{title}' beginnt mit '{prefix}:'. Der Aufgabentitel darf NICHT den Präfix '{prefix}' enthalten — er wird automatisch zum PR-Titel hinzugefügt. Aktualisieren Sie den Vikunja-Aufgabentitel, um den Präfix zu entfernen.",
"en": "Vikunja task title '{title}' starts with '{prefix}:'. The task title should NOT include the '{prefix}' prefix — it is automatically added to the PR title. Update the Vikunja task title to remove the prefix.",
"pl": "Tytuł zadania Vikunja '{title}' zaczyna się od '{prefix}:'. Tytuł zadania nie powinien zawierać prefiksu '{prefix}' — jest on automatycznie dodawany do tytułu PR. Zaktualizuj tytuł zadania Vikunja, aby usunąć prefiks.",
"ru": "Заголовок задачи Vikunja '{title}' начинается с '{prefix}:'. Заголовок задачи НЕ должен включать префикс '{prefix}' — он автоматически добавляется к заголовку PR. Обновите заголовок задачи Vikunja, чтобы удалить префикс.",
"zh": "Vikunja 任务标题 '{title}' 以 '{prefix}:' 开头。任务标题不应包含 '{prefix}' 前缀 — 它会自动添加到 PR 标题中。请更新 Vikunja 任务标题以删除前缀。"
}
}
+15
View File
@@ -292,3 +292,18 @@ class TestCli:
)
assert result.exit_code != 0
assert "does not match Vikunja" in result.output
def test_fails_with_double_prefix_in_vikunja_title(self) -> None:
"""Vikunja title with task ID prefix causes double-prefix in PR title."""
runner = CliRunner()
with (
patch.dict("os.environ", {"DEVX_TASK_PREFIX": "DEVX", "VIKUNJA_TOKEN": "tok"}, clear=True),
patch("devx.ci.check_auto_merge_ready.is_branch_behind_master", return_value=False),
patch("devx.ci.check_auto_merge_ready.get_vikunja_title_optional", return_value="DEVX-1: Fix foo"),
):
result = runner.invoke(
cli,
["--branch", "DEVX-1-fix-foo", "--pr-title", "DEVX-1: Fix foo"],
)
assert result.exit_code != 0
assert "should NOT include" in result.output